In this page I want to create my view to Blazor syntax, opportunity and main programming techniques. Similar review I was written in 2002-year, when Microsoft was created NET Framework 1.1, Visual Basic 7 and Classic ASP.NET - Как я осваивал .NET-framework., Как я осваивал .NET-студию., Язык программирования VB.NET and when I made important step in my life - step from VB6 to VB7. Then in 2010 I made next important step in my life - moving from Classic ASP.NET to ASP.NET MVC - Мой первый сайт на MVC 3 Razor.
And now, in 2020, I'm reading current Microsoft documentation to this technology - MS ASP.NET.pdf trying to moving to this techniques and I want to write my small notice review of Blazor syntax and opportunity.
Because MS always doing one step forward and two step backward, Blazor is some kind of reincarnation of Classic ASP.NET. This is a good news for me, because MS agreed with my deep feeling, than ASP.NET MVC with complex and sophisticated frontend like Angular was bee greatest mistake. 2020 year is good time of return to Classic ASP.NET.
This is documentation of Blazor for Classic ASP.NET programmer. It contains only couple page, because we have always understanding this technology.
This is scheme to configuring common NET Core environment and Blazor start page.
You can see (and parse!) only static page in browser. In the screen below you can understand what in Layout (Master Page in term of Classic ASP.NET) or for russian speaking its named "плашка сайта".
If page rendering dynamically, you can not see HTML at all. And Browser developer tools nothing to show. But If you connect browser through proxy (How to debug SignalR application in Net Core 3.1), you can see full SignalR traffic.
In Blazor HTML-page we can use directly any C# C# statements, operators, variables as Blazor expression (after "@" without generic or after "@()"), this is MS description of Blazor Expression.
We can inserting own C# code into Blazor lifeCycle in some place.
1: public virtual Task SetParametersAsync(ParameterView parameters);
2: protected virtual void BuildRenderTree(RenderTreeBuilder builder);
3: protected Task InvokeAsync(Action workItem);
4: protected Task InvokeAsync(Func<Task> workItem);
5: protected virtual void OnAfterRender(bool firstRender);
6: protected virtual Task OnAfterRenderAsync(bool firstRender);
7: protected virtual void OnInitialized();
8: protected virtual Task OnInitializedAsync();
9: protected virtual void OnParametersSet();
10: protected virtual Task OnParametersSetAsync();
11: protected virtual bool ShouldRender();
12: protected void StateHasChanged();
In Blazor we have some directives in HTML, for example in screen below you can see directive @OnClick. We can link own C# code by this way - assign string to directive.
Alternative way to link own C# function to HTML is Lambda expression @onclick=@(async ()=>await XXX(YYY))
This events calling in the green circle in diagram above, before OnAfterRendering events and this is MS description of ComponentBase context, available in C# code.
Unfortunately, Blazor not support Page OnLeave event, therefore need to emulate this event using Microsoft.AspNetCore.Components.Server.Circuits. How to do this please look in my code:
We have a lot of events to link own code, not only @OnClick.
Progress events | |
@onloadstart | ProgressEventArgs |
@ontimeout | ProgressEventArgs |
@onabort | ProgressEventArgs |
@onload | ProgressEventArgs |
@onloadend | ProgressEventArgs |
@onprogress | ProgressEventArgs |
@onerror | ErrorEventArgs |
General events | |
@onactivate | EventArgs |
@onbeforeactivate | EventArgs |
@onbeforedeactivate | EventArgs |
@ondeactivate | EventArgs |
@onended | EventArgs |
@onfullscreenchange | EventArgs |
@onfullscreenerror | EventArgs |
@onloadeddata | EventArgs |
@onloadedmetadata | EventArgs |
@onpointerlockchange | EventArgs |
@onpointerlockerror | EventArgs |
@onreadystatechange | EventArgs |
@onscroll | EventArgs |
Focus events | |
@onfocus | FocusEventArgs |
@onblur | FocusEventArgs |
@onfocusin | FocusEventArgs |
@onfocusout | FocusEventArgs |
Mouse events | |
@onmouseover | MouseEventArgs |
@onmouseout | MouseEventArgs |
@onmousemove | MouseEventArgs |
@onmousedown | MouseEventArgs |
@onmouseup | MouseEventArgs |
@onclick | MouseEventArgs |
@ondblclick | MouseEventArgs |
@oncontextmenu | MouseEventArgs |
@onwheel | WheelEventArgs |
@onmousewheel | WheelEventArgs |
Drag events | |
@ondrag | DragEventArgs |
@ondragend | DragEventArgs |
@ondragenter | DragEventArgs |
@ondragleave | DragEventArgs |
@ondragover | DragEventArgs |
@ondragstart | DragEventArgs |
@ondrop | DragEventArgs |
Clipboard events | |
@onbeforecopy | EventArgs |
@onbeforecut | EventArgs |
@onbeforepaste | EventArgs |
@oncopy | ClipboardEventArgs |
@oncut | ClipboardEventArgs |
@onpaste | ClipboardEventArgs |
Keyboard events | |
@onkeydown | KeyboardEventArgs |
@onkeyup | KeyboardEventArgs |
@onkeypress | KeyboardEventArgs |
Input events | |
@onchange | ChangeEventArgs |
@oninput | ChangeEventArgs |
@oninvalid | EventArgs |
@onreset | EventArgs |
@onselect | EventArgs |
@onselectstart | EventArgs |
@onselectionchange | EventArgs |
@onsubmit | EventArgs |
Touch events | |
@ontouchcancel | TouchEventArgs |
@ontouchend | TouchEventArgs |
@ontouchmove | TouchEventArgs |
@ontouchstart | TouchEventArgs |
@ontouchenter | TouchEventArgs |
@ontouchleave | TouchEventArgs |
Pointer events | |
@ongotpointercapture | PointerEventArgs |
@onlostpointercapture | PointerEventArgs |
@onpointercancel | PointerEventArgs |
@onpointerdown | PointerEventArgs |
@onpointerenter | PointerEventArgs |
@onpointerleave | PointerEventArgs |
@onpointermove | PointerEventArgs |
@onpointerout | PointerEventArgs |
@onpointerover | PointerEventArgs |
@onpointerup | PointerEventArgs |
Media events | |
@oncanplay | EventArgs |
@oncanplaythrough | EventArgs |
@oncuechange | EventArgs |
@ondurationchange | EventArgs |
@onemptied | EventArgs |
@onpause | EventArgs |
@onplay | EventArgs |
@onplaying | EventArgs |
@onratechange | EventArgs |
@onseeked | EventArgs |
@onseeking | EventArgs |
@onstalled | EventArgs |
@onstop | EventArgs |
@onsuspend | EventArgs |
@ontimeupdate | EventArgs |
@onvolumechange | EventArgs |
@onwaiting | EventArgs |
More detail about Event see please in MS Documentation ASP.NET Core Blazor event handling.
This is list of Page-Directives - MS Description.
And example of use it.
Also there are 5 Html-Directives. @bind and @on is metadirectives, @on mention in section above, @attributes in section below.
Usually I always create my own HTML-helper (directives in Blazor terminology) How to create Razor html-helper in VB.NET, but at the moment I write this notice I don't understand how to do this in Blazor.
Property and method in C# code can be marked by some attributes Microsoft.AspNetCore.Components Namespace.
We have four group of Blazor tags in the standard blazor namespaces (selected in _Imports.razor):
That is:
We can see example of using this tags in my public project templates:
In Blazor we can execute JS by the pattern below.
This is right way to calling JS.
But not standard library as jQuery and many other JS library has conflict with _framework/blazor.server.js, for example Pace.JS (because PACE.JS is part of blazor.server.js and all js-constants will be doubled). This message show PACE.JS, included in blazor.server.js
In opposite direction you can call Blasor component from JS.
At the moment Blazor has 8 tags related to jQuery, but officially I don't see any documentation about its. Maybe this is only empty stubs for future.
However I constantly use jQuery functionality in my Blazor projects and doing it in the same way - I use JS function getElementsByClassName
There are two different type of binding - binding HTML-element and binding builting Blazor components This is pattern of binding Html-element. Any HTML property or component parameters can be binding to the same way, Data must be parameters to binding XXX and EventCallback XXXChanged.
Another type of binding (with another keyword) is binding builting Blazor components inside form tag, inside Blazor <EditForm> tag. Also inside form can be present various validation, include custom validation, see below Forms and Validation.
And third case of binging, this is component binding. In this approach I have created custom component based on HTML-element and in high level it raise EventCallback UserChanged(UserID).
Binding this component on page looks as:
See full opportunity of custom component below Custom component future.
There some way to manipulating style of HTML elements. This is a first way, for open submenu I adding class "In".
This is a second way:
And third way is create own function with manipulation styles by boolean parameters in code.
There are some tags rendering to HTML-input elements.
DataAnnotation in Model is processing as usually automatically by DataAnnotationsValidator. And this is common patter to use it.
Instead MS DataAnnotationsValidator it's possible to create custom validation component in the pattern below, validator component nested from Abstract validator.
We can use form parameter Context, in this case we have opportunity to use many button with different function to the same form.
More details about validation see please in MS documentation - ASP.NET Core Blazor forms and validation and Forms and validation.
Of course we can create own components because any RAZOR file is components. Nut if components has route "@page" it has own URL, otherwise this is control (it ASP.NET classic terminology).
Blazor component (control) has some special future:
All this special future we can see in this my project template.
For each SPA page Blazor server has permanently SignalR connection and in existing connection we only can read connection paremeters by HttpAccessor. But any navigation to another page like NavigationManager.NavigateTo(URL, ForceReload=true) SignalR connection will be break and we can install any new connection parameters.
This opportunity I use to authentication, see my project template.
Even we can directly write to Browser like in simple ASP.NET, see this my project template.
Most programmer not understanding this simple Blazor future - why? Because this is common disadvantage of Dependency Injection technology. For example, you even add HttpAccessor, but you can not see property BodyWriter. Only after you add library Microsoft.AspNetCore.Http.Features needed property and methods will be appear.
Without dependency injection we simple was opened ObjectBrowser and was search needed symbol by seconds. But Nuget has millions and millions of libraries, how we will know what library contains needed method? Extension, extension and extension of existing framework is great advantage for framework developers but it not look greate for customer of expandable framework. Or need to create at least Object Browser for all nuget packages.
By the way dependency injection one of the common obstacle for fast programming, for example dumb Visual Studio not propose anything even in obviously case. But is it possible to remember any namespaces in millions on .Net framework classes?
We can imports all NET Core tags to blazor CHTML-file (not in Blazor !) - Tag Helpers in forms in ASP.NET Core, Built-in ASP.NET Core Tag Helpers.
After that will be accessible a lot of ancient (but useful HTML-helpers).
For example this helper generate input=text with additional attributes two-way binded to data.
@Html.EditorFor(model => model.YourProperty, new { htmlAttributes = new { @class="myCssClass", style="Width:100px" } }) @Html.ValidationSummary
Also there are more modern way to receive access to ASP.NET server functionality of this tags.
asp-action |
<a asp-controller="Speaker" asp-action="Evaluations">Speaker Evaluations</a>
|
asp-controller |
<a asp-controller="Speaker" asp-action="Index">All Speakers</a>
|
asp-area |
<a asp-area="Sessions" asp-page="/Index">View Sessions</a>
|
asp-fragment |
<a asp-controller="Speaker" asp-action="Evaluations" asp-fragment="SpeakerEvaluations">Speaker Evaluations</a>
|
asp-host |
<a asp-protocol="https" asp-host="microsoft.com" asp-controller="Home" asp-action="About">About</a>
|
asp-protocol |
<a asp-protocol="https" asp-controller="Home" asp-action="About">About</a>
|
asp-route |
<a asp-route="speakerevals">Speaker Evaluations</a>
[Route("/Home/Test", Name = "Custom")]
|
asp-all-route-data |
<a asp-route="speakerevalscurrent" asp-all-route-data="parms">Speaker Evaluations</a>
|
asp-route-* |
<a href="/Speaker/Detail?speakerid=12">SpeakerId: 12</a>
|
asp-page |
<a asp-page="/Attendee">All Attendees</a>
|
asp-page-handler |
<a asp-page="/Attendee" asp-page-handler="Profile" asp-route-attendeeid="12">Attendee Profile</a>
|
asp-action |
<button asp-controller="Home" asp-action="Index">Click Me</button>
<input type="image" src="..." alt="Or Click Me" asp-controller="Home" asp-action="Index">
<input type="submit" asp-controller="Home" asp-action="Index">Send</button>
<form asp-controller="Home" asp-action="Index">
|
asp-controller | ... |
asp-area | ... |
asp-page | ... |
asp-page-handler | ... |
asp-route | ... |
asp-route-{value} | ... |
asp-all-route-data | ... |
asp-fragment | ... |
cache-enabled |
<cache enabled="true">
|
<distributed-cache name="MyCache" enabled="true">
| |
cache-expires-on |
<cache expires-on="@new DateTime(2025,1,29,17,02,0)">
|
<distributed-cache name="MyCache" expires-on="@new DateTime(2025,1,29,17,02,0)">
| |
cache-expires-after |
<cache expires-after="@TimeSpan.FromSeconds(120)">
|
<distributed-cache name="MyCache" expires-after="@TimeSpan.FromSeconds(120)">
| |
cache-expires-sliding |
<cache expires-sliding="@TimeSpan.FromSeconds(60)">
|
<distributed-cache name="MyCache" expires-sliding="@TimeSpan.FromSeconds(60)">
| |
cache-vary-by-header |
<cache vary-by-header="User-Agent">
|
<distributed-cache name="MyCache" vary-by-header="User-Agent">
| |
cache-vary-by-query |
<cache vary-by-query="Make,Model">
|
<distributed-cache name="MyCache" vary-by-query="Make,Model">
| |
cache-vary-by-route |
<cache vary-by-route="Make,Model">
|
<distributed-cache name="MyCache" vary-by-route="Make,Model">
| |
cache-vary-by-cookie |
<cache vary-by-cookie=".AspNetCore.Identity.Application">
|
<distributed-cache name="MyCache" vary-by-cookie=".AspNetCore.Identity.Application">
| |
cache-vary-by-user |
<cache vary-by-user="true">
|
<distributed-cache name="MyCache" vary-by-user="true">
| |
cache-vary-by |
<cache vary-by="@Model">
|
cache-priority |
<cache priority="High">
|
<distributed-cache name="MyCache" priority="High">
|
environment-names |
<environment names="Staging,Production">
|
environment-include |
<environment include="Staging,Production">
|
environment-exclude |
<environment exclude="Development">
|
component |
<component type="typeof(ColorfulCheckbox)" render-mode="ServerPrerendered" param-Size="14" param-Color="@("blue")" />
|
asp-append-version |
<img src="~/images/asplogo.png" asp-append-version="true">
|
asp-antiforgery |
<form asp-controller="Home" asp-action="Index" asp-antiforgery="true" asp-route-returnurl="@ViewData["ReturnUrl"]" >
|
asp-for, asp-items |
<select asp-for="Country" asp-items="Model.Countries"></select>
|
asp-for |
<input asp-for="<Expression Name>">
|
asp-validation-for |
<span asp-validation-for="Email"></span>
|
asp-antiforgery |
<form asp-controller="Home" asp-action="Index" asp-antiforgery="true" >
|
Html.ValidationMessageFor |
<span asp-validation-for="Email"></span>
|
ValidationSummary.All/ModelOnly/None |
<div asp-validation-summary="ModelOnly"></div>
|
Some this tags we can directly use in our Blazor program, for example validation tags:
As in each NET Core projects in Blazor we can create service to provide needed data for site.
We can use service in NET CORE by three way - With MVC controller, without MVC controller and simple define any service as global variable in Program (RND in third project). You can see this three way in three my project templates:
We can use any service by timer - with simple system timer and Quartz service.
We can perform server API from Blazor project in different way - manually with definition HttpClient (as service or global reference in Program). And we can create list of server's API by SwaggerUI (this is example how to do this):
And than build by NSwagStudio separate layer to call Server API.
You can add to Blazor apps EF Core data access with various DB engine. Usually I use InMemoryDatabase - look more details in this page - How Linux Net Core 3.1 daemon (in VB.NET) can read/write data from/to ancient MS SQL 2005 and usually I use MySQL.
Right way to add EF Code with this database you can see in two my projects.
With Entity Framework we have two main pattern to accessing data. Firstly, if we working in concrete table context, in this case we usually use Linq. To working with dynamic LINQ Query usually exist a convenient way to create special extension. I show this way in my BlazorCRUD demo project.
And second choice (usually without concrete table context) - raw SQL statement. This way I show in BlazorDbBackup project (second screen from my real admin panel). In this page I has been read list on project tables.
In next page I read metadata from EF (fields description) and than show all rows.
Microsoft has no full support Database First in EF, this is big trouble from MS, because EF Code first is possible only with brand new project. But is you really working with full new projects? 99% of job is rebulding and modification existing project, in this case always need to apply Database first approach. Unfortunately MS do not admit this simple truth and continue promote CodeFirst technology. How to use Database Firts approach you can see in my page Transform Database from MsSQL to MySQL (Net Core EF DbFirst, Scaffold-DbContext, Read Db Schema, Move Users and data, EF SQL trace).
For accounting user Blazor perform ordinary NET Core classes Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext and classes derived from this class ApplicationDbContext:
But a lot of ordinary Net Core classes working with User Identity (like HttpContextAccessor, SignInManager<ApplicationUser>) is not working correctly in Blazor. Only classes with database (like UserManager<ApplicationUser>) still working in Blazor as in Net Core MVC.
Because Blazor use SignalR connection instead simple WebRequest/WebResponse, User Authentication is not similar to previous WebTechnology. There are two different methods of AU - with break existing socket connection and reestablishing it again with cookies with login after login will be processing by simple webRequest.
First method I have published in this project template:
And second method is use IdentityServer and JWT OWIN Token.
At the moment we have a lot of HTML-tags, but NET Core provide huge number of additional attributes. For example, HTML5 standard describe 25 attributes of HTML5-Button element: accesskey, autofocus, class, contenteditable, data-*, dir, disabled, draggable, form, formaction, formenctype, formmethod, formnovalidate, formtarget, hidden, id, lang, name, spellcheck, style, tabindex, title, translate, type, value.
But Blazor support 14 additional tags for the same HTML-Button element:
And, of cource, we can define own CSS attributes - this is common CSS future, for example in this project I define custom CSS to create tooltip.
Blazor is Net Core application, as each of NET Core application Blazor app has a lot builting internal services:
This three library usually mandatory to each Blazor project:
And you can see in Nuget a lot of various MS library and millions and millions of various third party libraries, each of those can be add to own project. For example for one my Blazor project I has been created this library:
Greatest library from MS is Microsoft.AspNetCore.Components.Desktop, it's MS clone of old Adobe idea to start Flex on Desktop (AIR application),this is my liked type of projects and I was create a lot of various AIR application, this is colorful and awesome alternatives for bored Windows Forms apps.
Fortunately Blazor application also is possible to start as desktop application with minimal changes.
Microsoft provide only couple of poor component for Blazor.
Its not seriously at all. Need to provide at least the same components that was in ASP.NET in 2002 year GridView, DataList and 50+ was present in MS AJAX package for Classic ASP.NET in 2005 year. Therefore other company can be created various useful to programmer components, for example look to this packages Radzen Blazor Components containes 40+ free components, Blazorise is a good package too.
Also other companies and peoples create various list of useful projects, for example look to this list Blazor Open Source Projects and Awesome Blazor.
Blazor extension Extensions, usually adding additional JavaScript like this Logging Extensions
Not all extension is great and usefull, for example this Logging Extensions is stupid and inexplicable for me, it can be replaced only one line of Javascript code. If you download my own Blazor project template, you will see only one line Javascript I replace this extension.
But there are a lot of very useful and sophisticated extension, in fact this is interop wrapper around sophisticated and complex JavaScrip framework, look for example to this extension:
In Blazor is missing some main components and future:
As a result, this restriction of ASP.NET Core Blazor technology give programmers only tiny percents of performance ASP.NET Classic programmers, in ASP.NET Core Blazor you will create for months the same site as Classic ASP.NET programmer could created during one day, but this obscure is full fit to Microsoft point of view - price of any software in the world must be increase, so that can be induce increase cost of Microsoft stock as software leader company.
<SITEMAP> <MVC> <ASP> <NET> <DATA> <KIOSK> <FLEX> <SQL> <NOTES> <LINUX> <MONO> <FREEWARE> <DOCS> <ENG> <MAIL ME> <ABOUT ME> < THANKS ME> |