Nothing Special   »   [go: up one dir, main page]

Blazor Framework

Download as pdf or txt
Download as pdf or txt
You are on page 1of 11

Roadmap: https://github.

com/dotnet/aspnetcore/issues/21514

Blazor components are .NET classes that represent a reusable piece of UI. Each component maintains its own state
and specifies its own rendering logic, which can include rendering other components. Components specify event
handlers for specific user interactions to update the component's state.

After a component handles an event, Blazor renders the component and keeps track of what changed in the
rendered output. Components don't render directly to the Document Object Model (DOM). They instead render to
an in-memory representation of the DOM called a RenderTree so that Blazor can track the changes. Blazor
compares the newly rendered output with the previous output to calculate a UI diff that it then applies efficiently to
the DOM.

The components render, and the calculated UI diff is serialized and sent to the browser where it's applied to the DOM.

Root component

In the Blazor Server app, the root component’s host page is defined in the *_Host.cshtml* file. This file defines a Razor
Page, not a component. Razor Pages use Razor syntax to define a server-addressable page, very much like an .aspx
page. The Html.RenderComponentAsync<TComponent>(RenderMode) method is used to define where a root-level
component should be rendered.

Blazor, the app must specify


● where on the page the root component (App.Razor) should be rendered
Html.RenderComponentAsync<TComponent>(RenderMode)
● Add the corresponding Blazor framework scrip
1) browser only knows how to run WebAssembly (.wasm files),which is a specification published by the W3C (World
Wide Web Consortium).
2) code is compiled into a .dll, which is a managed code. Blazor will not only deploy your application's .dlls to the
browser, but also Mono must be downloaded by the browser.
3) The Mono team managed to port Mono to WebAssembly in the form of a Mono.wasm file, which is deployed to the
browser. Then Mono IL (Intermediate Language) is used to run our application's managed code.

Loading steps

1. First, blazor.js is loaded


2. Blazor.js uses Mono's JavaScript library (mono.js)
3. Mono.js loads Mono WebAssembly runtime (mono.wasm)
4. Mono.wsm, in turn, loads our application DLLs (BlazorBricks and BlazorBricks.Core) and the .NET
Framework DLLS.

:::no-loc(Blazor WebAssembly):::

The principal hosting model for :::no-loc(Blazor)::: is running client-side in the browser on WebAssembly. The
:::no-loc(Blazor)::: app, its dependencies, and the .NET runtime are downloaded to the browser. The app is executed
directly on the browser UI thread. UI updates and event handling occur within the same process. The app's assets are
deployed as static files to a web server or service capable of serving static content to clients. Because the app is
created for deployment without a backend ASP.NET Core app, it's called a standalone :::no-loc(Blazor WebAssembly):::
app .

:::no-loc(Blazor Server):::

With the :::no-loc(Blazor Server)::: hosting model, the app is executed on the server from within an ASP.NET Core app.
UI updates, event handling, and JavaScript calls are handled over a :::no-loc(SignalR)::: connection.
Circuits

A :::no-loc(Blazor Server)::: app is built on top of ASP.NET Core :::no-loc(SignalR):::. Each client communicates to the server over
one or more :::no-loc(SignalR)::: connections called a circuit . A circuit is :::no-loc(Blazor):::'s abstraction over :::no-loc(SignalR):::
connections that can tolerate temporary network interruptions. When a :::no-loc(Blazor)::: client sees that the :::no-loc(SignalR):::
connection is disconnected, it attempts to reconnect to the server using a new :::no-loc(SignalR)::: connection.
HTML Generation

Interpreted mode

In interpreted mode, the Mono runtime itself is compiled to WebAssembly, but your .NET assembly files are
not. The browser can then load and execute the Mono runtime, which in turn can load and execute standard
.NET assemblies (regular .NET .dll files) built by the normal .NET compilation toolchain.

This is similar to how, for the desktop CLR, the core internals of the CLR are distributed precompiled to native
code, which then loads and executes .NET assembly files. One key difference is that the desktop CLR uses
just-in-time (JIT) compilation extensively to make execution faster, whereas Mono on WebAssembly is closer
to a pure interpretation model.

Ahead-of-time (AOT) compiled mode

In AOT mode, your application’s .NET assemblies are transformed to pure WebAssembly binaries at build
time. At runtime, there’s no interpretation: your code just executes directly as regular WebAssembly code. It’s
still necessary to load part of the Mono runtime (e.g., parts for low-level .NET services like garbage
collection), but not all of it (e.g., parts for parsing .NET assemblies).
This is similar to how the ngen tool has historically allowed AOT compilation of .NET binaries to native
machine code, or more recently, CoreRT provides a complete native AOT .NET runtime.

Interpreted versus AOT

Which mode is best? We don’t know yet.

What we do know is that interpreted mode provides a much faster development cycle than AOT. When you
change your code, you can rebuild it using the normal .NET compiler and have the updated application
running in your browser in seconds. An AOT rebuild, on the other hand, might take minutes.

So one obvious thought is that interpreted mode might be for development, and AOT mode might be for
production.

But that might not turn out to be the case, because interpreted mode is surprisingly much faster than you’d
think, and we hear from Xamarin folks who use .NET for native mobile apps that regular (non-AOT) .NET
assemblies are very small and compression-friendly compared with AOT-compiled assemblies. We’re keeping
our options open until we can measure the differences objectively.

Request a service in a component


Once services are added to the service collection, they can be injected into the components' Razor templates
using the @inject Razor directive. @inject has two parameters:

● Type name: The type of the service to inject.


● Property name: The name of the property receiving the injected app service. Note that the property
doesn't require manual creation. The compiler creates the property.

Multiple @inject statements can be used to inject different services.


The following example shows how to use @inject. The service implementing Services.IDataAccess is injected
into the component's property DataRepository. Note how the code is only using the IDataAccess abstraction:

@page "/customer-list"
@using Services
@inject IDataAccess DataRepository

Dependency injection in services

public void ConfigureServices(IServiceCollection services)


{
services.AddTransient<IRepository, Repository>();
services.AddTransient<MyTransientService, MyTransientService>();
services.AddScoped<MyScopedService, MyScopedService>();
services.AddSingleton<MySingletonService, MySingletonService>();
}

Complex services might require additional services. In the prior example, DataAccess might require Blazor's default
service HttpClient. @inject or the InjectAttribute can't be used in services. Constructor injection must be used instead.
Required services are added by adding parameters to the service's constructor. When dependency injection creates the
service, it recognizes the services it requires in the constructor and provides them accordingly.
The following code sample demonstrates the concept:

public class DataAccess : IDataAccess


{
// The constructor receives an HttpClient via dependency
// injection. HttpClient is a default service offered by Blazor.
public DataAccess(HttpClient client)
{
...
}
...
}

Note the following prerequisites for constructor injection:


● There must be one constructor whose arguments can all be fulfilled by dependency injection. Note that
additional parameters not covered by DI are allowed if default values are specified for them.
● The applicable constructor must be public.
● There must only be one applicable constructor. In case of an ambiguity, DI throws an exception.

AuthorizeView

<AuthorizeView>
<Authorized>
<a href="Identity/Account/Manage">Hello, @context.User.Identity.Name!</a>
<a href="Identity/Account/LogOut">Log out</a>
</Authorized>
<NotAuthorized>
<a href="Identity/Account/Register">Register</a>
<a href="Identity/Account/Login">Log in</a>
</NotAuthorized>
</AuthorizeView>

The AuthorizeView component will only display its child content when the user is authorized. Alternatively, the
AuthorizeView takes parameters for specifying different templates when the user is Authorized, NotAuthorized, or
Authorizing

Registering authentication

With ASP.NET Core Identity things are actually straightforward and there’s no reason to discuss it. It’s more interesting
to find out how things are configured in Startup class.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {

app.UseAuthentication();
app.UseAuthorization();
}

Passing References of Elements


Since Blazor 0.5.0 you can also pass references of HTML elements from C# to JavaScript
functions. All you have to do is adding the attribute ref to the HTML elements and then add an
ElementRef field with the value of the attribute in the functions section.

<textarea ref="text"></textarea>

@functions {
ElementRef text;
}

In this example we create a reference text for a textarea element. The ElementRef handle is completely opaque for
Blazor, so you cannot do anything with itm except passing as-it-is to a JavaScript function.

@function {
JSRuntime.Current.InvokeAsync<string>( "console.log", text);
}

Components

Each Blazor page is a BlazorComponent, and as such it follows the component lifecycle: first, it receives parameters from its
parent in the render tree. Then, the OnInitAsync method of the Index.cshtml page is invoked. But how do we implement
this method in C# inside the Blazor .cshtml file? We must include our C# code for that page in a @functions directive:
Hide Copy Code
@functions {
protected override async Task OnInitAsync()
{
}
}

JavaScript/TypeScript interop
To call other JS libraries or your own custom JS/TS code from .NET, the current approach is to register a named
function in a JavaScript/TypeScript file, e.g.:

// This is JavaScript
Blazor.registerFunction('doPrompt', message => {
return prompt(message);
});

and then wrap it for calls from .NET:


// This is C#
public static bool DoPrompt(string message)
{
return RegisteredFunction.Invoke<bool>("doPrompt", message);
}

The registerFunction approach has the benefit of working nicely with JavaScript build tools such as Webpack.
And to save you the trouble of doing this for standard browser APIs, the Mono team is working on a library that exposes
standard browser APIs to .NET.

Component parameters
Components can have component parameters, which are defined using non-public properties on the
component class decorated with [Parameter]. Use attributes to specify arguments for a component in
markup.

ParentComponent.cshtml:
@page "/ParentComponent"
<h1>Parent-child example</h1>
<ChildComponent Title="Panel Title from Parent">
Child content of the child component is supplied by the parent component.
</ChildComponent>

ChildComponent.cshtml:

<div class="panel panel-success">


<div class="panel-heading">@Title</div>
<div class="panel-body">@ChildContent</div>
</div>

@functions {
[Parameter]
private string Title { get; set; }
[Parameter]
private RenderFragment ChildContent { get; set; }
}

Child content
Components can set the content in another component. The assigning component provides the content
between the tags that specify the receiving component. For example, a ParentComponentcan provide content
that is to be rendered by a ChildComponent by placing the content inside <ChildComponent> tags.

ParentComponent.cshtml:
@page "/ParentComponent"

<h1>Parent-child example</h1>

<ChildComponent Title="Panel Title from Parent">


Child content of the child component is supplied by the parent component.
</ChildComponent>

The child component has a ChildContent property that represents a RenderFragment. The value of
ChildContent is positioned in the child component's markup where the content should be rendered. In the
following example, the value of ChildContent is received from the parent component and rendered inside the
Bootstrap panel's panel-body.

ChildComponent.cshtml:
<div class="panel panel-success">
<div class="panel-heading">@Title</div>
<div class="panel-body">@ChildContent</div>
</div>

@functions {
[Parameter]
private string Title { get; set; }

[Parameter]
private RenderFragment ChildContent { get; set; }
}

NOTE

The property receiving the RenderFragment content must be named ChildContent by convention.
Lifecycle methods
OnInitAsync and OnInit execute code after the component has been initialized. To perform an asynchronous operation,
use OnInitAsync and use the await keyword:
protected override async Task OnInitAsync()
{
}
For a synchronous operation, use OnInit:
protected override void OnInit()
{
}

OnParametersSetAsync and OnParametersSet are called when a component has received parameters from its parent
and the values are assigned to properties. These methods are executed after OnInit during component initialization.
protected override async Task OnParametersSetAsync()
{
}
protected override void OnParametersSet()
{
}

OnAfterRenderAsync and OnAfterRender are called each time after a component has finished rendering. Element and
component references are populated at this point. Use this stage to perform additional initialization steps using the
rendered content, such as activating third-party JavaScript libraries that operate on the rendered DOM elements.
protected override async Task OnAfterRenderAsync()
{
}

protected override void OnAfterRender()


{
}

SetParameters can be overridden to execute code before parameters are set:


public override void SetParameters(ParameterCollection parameters)
{
...

base.SetParameters(parameters);
}

If base.SetParameters isn't invoked, the custom code can interpret the incoming parameters value in any way required.
For example, the incoming parameters aren't required to be assigned to the properties on the class.
ShouldRender can be overridden to suppress refreshing of the UI. If the implementation returns true, the UI is
refreshed. Even if ShouldRender is overridden, the component is always initially rendered.
protected override bool ShouldRender()
{
var renderUI = true;
return renderUI;
}

ShouldRender
You can override ShouldRender to suppress refreshing of the UI. If your implementation returns true, UI is refreshed.
Otherwise, changes are not propagated to the UI. Note that an initial rendering is always performed, independent of the
return value of ShouldRender.

Set async method


<button () => await PrintWebApiResponse())>Print Web API Response</button>

Parent form

<div id="search-area" class="px-2 py-3">


<Search />
</div>

Child form
<button => OnSearch(criteria)) type="button" class="btn btn-danger px-5">
Search ➝
</button>

@functions
{
[Parameter] Func<SearchCriteria, Task> OnSearch { get; set; }
}

You might also like