Intro
Though I have been interested in Blazor, I have never tried it.
But in last update(ASP.NET Core Preview 7), there were many update about Blazor.
So I decided trying Blazor.
Because if I actually use Blazor, I want to add it into existing projects, so I create an MVC project first.
Blazor has two types( Blazor Server and Blazor WebAssembly).
Because I don't try PWA and I feel Blazor Server looks like more easy to understand, so I choose Blazor Server this time.
Environments
- ASP.NET Core ver.5.0.100-preview.7.20366.6
Base project
I create a empty project.
And I add Controller and Razor files.
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace BlazorSample
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseStaticFiles();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
HomeController.cs
using Microsoft.AspNetCore.Mvc;
using Models;
namespace Controllers
{
public class HomeController: Controller
{
[Route("/")]
public ActionResult Index()
{
return View("Views/Index.cshtml");
}
}
}
Product.cs
namespace Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
}
_ViewStart.cshtml
@{
Layout = "_Layout";
}
_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewData["Title"]</title>
</head>
<body>
@RenderBody()
</body>
</html>
Index.cshtml
@{
ViewData["Title"] = "Home Page";
}
<h2>This is Index.cshtml</h2>
Add Blazor
According the article, I can add Blazor into the project.
Add middleware and mapping Blazors SignalR hub
Startup.cs
...
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseStaticFiles();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub();
});
}
...
_Imports.razor
@using System
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop
@using BlazorSample
BlzrSample.razor
<h1>Hello @Name!</h1>
<p>This is Blazor Components.</p>
<div>@SampleInfo</div>
<button @onclick="ShowConsole">Click</button>
@code {
[Parameter]
public string Name {get; set;}
[Parameter]
public string SampleInfo { get; set; }
public string GetName(){
var name = $"{Name} {DateTime.Now}";
Console.WriteLine(name);
return name;
}
public void ShowConsole()
{
SampleInfo = GetName();
Console.WriteLine($"{SampleInfo}");
}
}
Index.cshtml
@using BlazorSample.Views
@{
ViewData["Title"] = "Home Page";
}
<h2>This is Index.cshtml</h2>
@(await Html.RenderComponentAsync<BlzrSample>(RenderMode.ServerPrerendered, new { Name = "BlazorSample" }))
Result
When I click the button, div element value will be changed dynamically.
IE11
Though IE11 can render the page, the click event isn't worked.
One of the solution is using Blazor.Polyfill.
- GitHub - Daddoon/Blazor.Polyfill: Polyfills for Blazor (for Internet Explorer 11 support and some other browsers)
- ASP.NET Core Blazor supported platforms | Microsoft Docs
_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewData["Title"]</title>
<base href="/">
<script type="text/javascript">
if (/MSIE \d|Trident.*rv:/.test(navigator.userAgent)) {
document.write('<script src="https://polyfill.io/v3/polyfill.min.js?features=Element.prototype.closest%2CIntersectionObserver%2Cdocument.querySelector%2Cfeatures=Array.prototype.forEach%2CNodeList.prototype.forEach"><\/script>');
document.write('<script src="js/blazor.polyfill.min.js"><\/script>');
}
</script>
</head>
<body>
@RenderBody()
<script src="_framework/blazor.server.js"></script>
</body>
</html>
I must add "base" tag or get an error.
Control DOM
C#(Blazor) methods can't access DOM directly except using @foreach, etc.
@* when I add or remove items into the "Numbers",
the numbers of div elements will be changed *@
@foreach(var n in Numbers)
{
<div>@SampleInfo</div>
}
So I should call JavaScript methods to control DOM elements.
Interacts with JavaScript
Call JavaScript from Blazor
I can call JavaScript methods by IJSRuntime.
Because I usually use, I add webpack and TypeScript.
Samples
package.json
{
"dependencies": {
"ts-loader": "^8.0.2",
"tsc": "^1.20150623.0",
"typescript": "^3.9.7",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12"
}
}
webpack.config.js
var path = require('path');
module.exports = {
mode: 'development',
entry: {
'blazorSample': './wwwroot/ts/blazor-sample.ts',
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, './wwwroot/js'),
library: 'Page',
libraryTarget: 'umd'
}
};
blazor-sample.ts
export function callFromBlazor(name: string): string
{
console.log("Hello Blazor");
return `Hello Blazor ${name}`;
}
BlzrSample.razor
@inject IJSRuntime JSRuntime;
...
<button @onclick="CallFromBlazor">Click2</button>
@code {
...
private async Task CallFromBlazor()
{
var result = await JSRuntime.InvokeAsync<string>("Page.callFromBlazor", SampleInfo);
Console.WriteLine(result);
}
}
Call C# static methods from JavaScript
I can call c# static methods by DotNet.invokeMethodAsync.
First, I add type definition.
npm install --save @types/blazor__javascript-interop
blazor-sample.ts
...
export function callStaticFromJs() {
// 1. Assembly name, 2. Method name, 3. Arguments
DotNet.invokeMethodAsync("BlazorSample", "CallStaticFromJs", "FromJS")
.then(result => console.log(result));
}
BlzrSample.razor
...
<button onclick="Page.callStaticFromJs()">Click3</button>
@code {
...
// must add JSInvokable and set public static method.
[JSInvokable]
public static async Task<string> CallStaticFromJs(string message)
{
Console.WriteLine($"Hello JS {message}");
return await Task.FromResult("Hello static");
}
...
Call C# instance methods from JavaScript
To call C# instance methods, I shall create instance in C# methods, and send it to JavaScript methods.
HelloHelper.cs
using Microsoft.JSInterop;
public class HelloHelper
{
public HelloHelper(string name)
{
Name = name;
}
public string Name { get; set; }
// call from JavaScript
[JSInvokable]
public string SayHello() => $"Hello, {Name}!";
}
blazor-sample.ts
...
// argument: HelloHelper instance
export function callInstanceFromJs(dotnetHelper: any) {
return dotnetHelper.invokeMethodAsync('SayHello')
.then((r: any) => console.log(r));
}
BlzrSample.razor
@inject IJSRuntime JSRuntime;
...
<button @onclick="CallInstanceFromJs">Click4</button>
@code {
...
public async Task CallInstanceFromJs()
{
using(var helperRef = DotNetObjectReference.Create(new HelloHelper("Js instance")))
{
await JSRuntime.InvokeAsync<string>(
"Page.callInstanceFromJs", helperRef);
}
}
...
Though I use using clause to create instance, in actual usages, I may separate disposing the instance from the creating method.
Call server side methods
How to call server side methods from Blazor?
Blazor can use DI.
So I can inject service classes or any other classes.
IBlazorService.cs
using System.Threading.Tasks;
using Models;
namespace Services
{
public interface IBlazorService
{
Task<Product> GetMessageAsync(string Name);
}
}
BlazorService.cs
using System.Threading.Tasks;
using Models;
namespace Services
{
public class BlazorService: IBlazorService
{
public async Task<Product> GetMessageAsync(string name)
{
return await Task.FromResult(new Product
{
Id = 2,
Name = name,
});
}
}
}
Startup.cs
...
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddControllers();
services.AddScoped<IBlazorService, BlazorService>();
}
...
BlzrSample.razor
@inject Services.IBlazorService _blazor
...
<button @onclick="CallServerside">Click5</button>
@code {
...
public async Task CallServerside()
{
var result = await _blazor.GetMessageAsync(Name);
if (result == null)
{
Console.WriteLine("Cannot get message");
return;
}
Console.WriteLine($"Success ID: {result.Id} Name: {result.Name}");
}
}
Top comments (2)
I love Blazor and I started applying it in a real project. This post is a great help for me.
Thanks ♥
Thank you for reading my post.
Because I'm new to writing "Blazor" application, so I want to try more :)