Front

Mateusz Turzyński / Michał Michalczuk

What's Blazor?

Michał
Michalczuk

Mateusz
Turzyński

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">
  Click me
</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

?

App.dll

.NET

(mscorlib.dll, System.core.dll etc)

WebAssembly

(dotnet.wasm)

.cs

.razor

Compilation

What can Blazor do?

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<InputNumber @bind="@currentCount" />

<button class="btn btn-primary" @onclick="IncrementCount">
  Click me
</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

A day in the life of a software developer

let's create a form :)

public class PersonModel
{
	[Required]
	[Range(18, 60, 
    	ErrorMessage = "Either too old or too young :(")]
	public int Age { get; set; }

	[Required]
	[MinLength(5)]
	[MaxLength(15)]
	public string FullName { get; set; }
    
	public string Password { get; set; }

	[Compare(nameof(Password))]
	public string RepeatPassword { get; set; }    
}
@inject HttpClient Http

@code {
    private PersonModel person = new PersonModel();
    
    private async Task HandleValidSubmit()
    {
        await Http.PostJsonAsync("/api", person);
    }

    private void HandleInvalidSubmit()
    {
        Console.WriteLine("The form is not valid :(");
    }
}
<EditForm Model="person"
          OnValidSubmit="@HandleValidSubmit"
          OnInvalidSubmit="@HandleInvalidSubmit">

    <DataAnnotationsValidator />
    <ValidationSummary />

    <h1>Personal form</h1>

    <p>
        <label>Enter your age:</label>
        <InputNumber @bind-Value="person.Age" />
    </p>
    <p>
        <label>Enter your name:</label>
        <InputText @bind-Value="@person.FullName" />
        <ValidationMessage For="@(() => person.FullName)" />
    </p>
    
    (...)
    
    <p>
        <button type="submit">Submit</button>
    </p>

</EditForm>

Style

// MyComponent.razor
<h1>My Component</h1>

// MyComponent.razor.css
h1 {
    color: red;
}

// generated code in scoped.styles.css
[b-dew6pvofzw] h1 {
	color: red;
}
<Styled @bind-Classname="@styledClass">
  color: red;
  background: @color;
</Styled>

<h1 class="@styledClass">Hello!</h1>

@code {
    private string color = "pink";
    private string styledClass;
}


// result in browser
<h1 class="epav-hwc">Hello!</h1>

👍

Routing

@page "/simple-routing/{text}"
@page "/advanced-routing/{counter:int}"

<p>Text from parameter: @Text</p>
@if (Counter.HasValue)
{
    <p>Counter: @Counter</p>
}

@code {

    [Parameter]
    public string Text { get; set; }

    [Parameter]
    public int? Counter { get; set; }
}
<NavLink href="simple-form" 
         Match="NavLinkMatch.All" 
         class="nav-link">
    Simple form
</NavLink>
<NavLink href="simple-routing/test" 
         Match="NavLinkMatch.Prefix" 
         class="nav-link" >
    Simple routing
</NavLink>

Components

<div>

    <p>Hello @FullName</p>

    <button @onclick="OnDeleteAccount">
        Delete my account
    </button>

</div>

@code {

    [Parameter]
    public string FullName { get; set; }

    [Parameter]
    public EventCallback OnDeleteAccount { get; set; }
}
@page "/person"

@if (accountDeleted)
{
    <p>Account deleted</p>
}
else
{
    <PersonDetails FullName="John Rambo"
                   OnDeleteAccount="() => AccountDeleted()" />
}

@code {

    private bool accountDeleted;

    private void AccountDeleted()
    {
        accountDeleted = true;
    }
}

How to talk with JavaScript?

JavaScript interop

// index.html
<script>
  window.getElementDimensions = selector => {
      try {
          const element = document.querySelector(selector);

          if (!element) {
              return null;
          }

          return {
              height: element.scrollHeight,
              width: element.scrollWidth
          };
      } catch (ex) {
          return null;
      }
  };
</script>
// UseJavaScript.razor
@inject IJSRuntime JSRuntime;

...

@code {
    class BrowserSize {
        public int Height { get; set; }
        public int Width { get; set; }
    }

    private string Selector { get; set; } = "body";
    private BrowserSize ClientSize { get; set; } = new BrowserSize();

    private async Task UpdateSize() 
    {
        var jsResult = await JSRuntime.InvokeAsync<BrowserSize>(
            "getElementDimensions", 
            Selector
        );

        ClientSize = jsResult != null ? jsResult : new BrowserSize();

        StateHasChanged();
    }
}

3rd party JS

// index.js
import { Chart } from 'chart.js';
import 'chartjs-plugin-labels';

export const charts = {
  drawChart: (canvasSelector, type, { data = [], labels = [] }) => {
    const canvas = document.querySelector(canvasSelector).getContext('2d');

    new Chart(canvas, {
      ....
    });
  }
};
const path = require('path');

module.exports = {
  module: {
    rules: [
      {
        test: /\.(js)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  },
  output: {
    path: path.resolve(__dirname, '../wwwroot/js'),
    filename: 'js-lib.js',
    library: 'FrontNetJS'
  }
};
// index.html
<script src="js/js-lib.js"></script>
@inject IJSRuntime JSRuntime;

<canvas @ref="charCanvas"></canvas>

@code {
    private ElementReference charCanvas;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender) 
        {
            await InitializeChart();
        }
    }

    private async Task InitializeChart() 
    {
        await JSRuntime.InvokeVoidAsync("FrontNetJS.charts.drawChart", 
            charCanvas, 
            "pie",
            new {
                Data = new List<int> { 100, 200, 300, 400},
                Labels = new List<string> { "A", "B", "C", "D" }
            }
        );

        StateHasChanged();
    }
}

Browser

UI Thread

Blazor

Blazor is single thread

What's more?

Lazy loading

<ItemGroup>
  <BlazorWebAssemblyLazyLoad Include="GrantImaharaRobotControls" />
</ItemGroup>
----
@inject LazyAssemblyLoader assemblyLoader

<Router AppAssembly="@typeof(Program).Assembly" 
    AdditionalAssemblies="@lazyLoadedAssemblies" 
    OnNavigateAsync="@OnNavigateAsync"
>
    <Navigating>...</Navigating>
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>...</NotFound>
</Router>

@code {
    private List<Assembly> lazyLoadedAssemblies = new List<Assembly>();

    private async Task OnNavigateAsync(NavigationContext args)
    {
        if (args.Path.EndsWith("/robot"))
        {
            var assemblies = await assemblyLoader.LoadAssembliesAsync(
                new List<string>() { "GrantImaharaRobotControls.dll" });
            lazyLoadedAssemblies.AddRange(assemblies);
        }
    }
}

Progressive Web Apps

Blazor Xamarin

<StackLayout>
    <Label FontSize="30"Text="Hello!"/>
    <Button Text="+1"OnClick="@HandleClick" />
</StackLayout>

@code {
    int count;

    void HandleClick()
    {
        count++;
    }
}
<StackLayout>
    <StackLayout Margin="new Thickness(20)">
        <Label 
          Text="Hello!" 
          FontSize="30" />
        <Button 
          Text="Increment from native" 
          OnClick="@CounterState.IncrementCount" 
          Padding="10" />
    </StackLayout>

    <BlazorWebView 
      ContentRoot="WebUI/wwwroot" 
      VerticalOptions="
       LayoutOptions.FillAndExpand
      "
    >
        <FirstBlazorHybridApp.WebUI.App />
    </BlazorWebView>
</StackLayout>

What's Blazor?

it's a .NET runtime 

it's a SPA framework

Blazor Web Assembly is finally production ready...

...so is Blazor Server

Blazor Server - How it works

JavaScript interop

Blazor WASM

  • runs client-side
  • heavy assets
  • no communication overhead
  • requires server-based API
  • doesn't require much server infrastructure
  • supports lazy loading

Blazor Server

  • runs server-side
  • light assets
  • generates heavy WS traffic

  • can act as an API server

  • doesn't scale well

  • no need for lazy loading

vs

Where to use Blazor Web Assembly

Nowadays: corporate systems, PWA

Future: performance-sensitive apps (see AoT) 

Where to use Blazor Server

Nowadays: intranet, dashboards

Future: since Blazor WASM is released we don't really see room for Blazor Server

Back in 2019

Final thoughts

  • Common code base
  • Vendor lock
  • Strong, static typing
  • There is no runaway from JS
  • Rich JS ecosystem is available
  • Super easy for backend developers
  • Young community
  • Ready to use in no time
  • Microsoft is heavily investing in Blazor

Thank you

Survey