Blazor .NET Core 3.1

De Banane Atomic
Aller à la navigationAller à la recherche

Liens

Description

  • Développement frontend avec C#
  • Intégration des bibliothèques .NET existantes (nuget)

WebAssembly

Permet d'exécuter du bytecode (code intermediaire) dans le navigateur grâce à la javascript runtime sandbox.
WebAssembly est nativement présent dans les navigateurs moderne.
WebAssembly possède un runtime .NET (mono.wasm), ce qui permet d'exécuter des assemblies .NET dans le navigateur.

Créer une application

Bash.svg
# Blazor WebAssembly
dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.1.0-preview4.19579.2
dotnet new blazorwasm -o blazorwasm
cd blazorwasm
dotnet run

# Blazor server
dotnet new blazorserver -o blazorserver
cd blazorserver
dotnet run

Hosting models

Blazor WebAssembly

L'application est téléchargée et exécutée dans le navigateur.

  • Excellentes performances
  • Pas besoin d'un serveur web ASP.NET Core pour héberger l'application
  • Permet une utilisation offline une fois chargée
  • Le temps de chargement est long, il faut télécharger l'application, ses dépendances et le .NET runtime
  • Les identifiants pour les accès aux bdd et web API sont chargés par chaque client

Blazor Server

L'application est exécutée sur le serveur. UI updates, event handling et les appels JavaScript sont pris en charge grâce à une connexion SignalR.

  • Pas besoin de WebAssembly
  • Chaque interaction utilisateur nécessite un appel au serveur
  • Un serveur web ASP.NET Core est nécessaire pour héberger l'application

Blazor server application

C'est une ASP.NET Core application.

Arborescence de fichiers

  • Program.cs: point d'entrée, appelle Startup.cs
  • Startup.cs: configuration (DI)
  • Pages: Components
  • Shared: shared Components comme les layouts (main, menu)
  • App.razor: routing
  • _Imports.razor: import namespaces
  • wwwroot: static files (css, js)
  • Data: service to access data

Debug

Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    // avoir des erreurs détaillées
    services.AddServerSideBlazor()
            .AddCircuitOptions(options => { options.DetailedErrors = true; });

Code in the same page vs Code-behind

Code in the same page

MyPage.razor
@page "/mypage"
<h1>Title</h1>

@code {
    // some C# code
}

Code-behind

MyPage.razor
@page "/mypage"
@inherits MyPageBase
<h1>Title</h1>
MyPageBase.cs
public class MyPageBase : ComponentBase
{
    // some C# code
}

Components

MyComponent.razor
@* si c'est une page, son url *@
@page "/employee"
@* si c'est une page, son url avec un parameter *@
@page "/employee/{EmployeeId}"

@code {
    [Parameter]
    public string EmployeeId { get; set; }
}

Layout

App.razor
<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        @* définit le layout par défaut *@
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>
Shared/MainLayout.razor
@inherits LayoutComponentBase

<div class="sidebar">
    <NavMenu />
</div>

<div class="main">
    <div class="content px-4">
        @Body
    </div>
</div>

Data Service

Dependency Injection

Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    // configuration du container de DI
    services.AddHttpClient<IItemDataService, ItemDataService>(
        client => client.BaseAddress = new Uri("http://localhost:5002")
    );

Data Service

Services/IItemDataService
public interface IItemDataService
{
    Task<IEnumerable<Item>> GetAllItemsAsync();
}
Services/ItemDataService
public class ItemDataService : IItemDataService
{
    private readonly HttpClient _httpClient;

    // injection de HttpClient
    public ItemDataService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<IEnumerable<Item>> GetAllItemsAsync()
    {
        return await JsonSerializer.DeserializeAsync<IEnumerable<Item>>(
            await _httpClient.GetStreamAsync($"api/item"),
            new JsonSerializerOptions()
            {
                PropertyNameCaseInsensitive = true
            });
    }

Component

Pages/MyComponentBase.cs
public class MyComponentBase : ComponentBase
{
    // injection de IItemDataService (cette syntaxe n'est possible que dans les components)
    [Inject]
    public IItemDataService ItemDataService { get; set; }

    public IEnumerable<Item> Items { get; set; }

    protected override async Task OnInitializedAsync()
    {
        Items = await ItemDataService.GetAllItemsAsync();
    }

Form

MyForm.razor
<EditForm Model="@Item"
          OnValidSubmit="@HandleValidSubmit"
          OnInvalidSubmit="@HandleInvalidSubmit>

    <button type="submit">Save</button>            
</EditForm>

Input Text

Razor.svg
<div class="form-group row">

    <label for="name"
           class="col-sm-3">
        Item: 
    </label>

    <InputText id="name"
               @bind-Value="@Item.Name"
               class="form-control col-sm-8"
               placeholder="Enter name">
    </InputText>

    <ValidationMessage class="offset-sm-3 col-sm-8"
                       For="@(() => Item.Name)" />
</div>

Input Text Area

Bash.svg
<div class="form-group row">
    <label for="comment"
           class="col-sm-3">
        Comment: 
    </label>
    <InputTextArea id="comment"
                   class="form-control col-sm-8"
                   @bind-Value="@Item.Comment"
                   placeholder="Enter comment">
    </InputTextArea>
    <ValidationMessage class="offset-sm-3 col-sm-8"
                       For="@(() => Item.Comment)" />
</div>

Input Number

Razor.svg
<div class="form-group row">

    <label for="name"
           class="col-sm-3">
        Item: 
    </label>

    <InputText id="name"
               @bind-Value="@Item.Name"
               class="form-control col-sm-8"
               placeholder="Enter name">
    </InputText>

    <ValidationMessage class="offset-sm-3 col-sm-8"
                       For="@(() => Item.Name)" />
</div>

Input Date

Razor.svg
<div class="form-group row">

    <label for="date"
           class="col-sm-3">
        Date: 
    </label>

    <InputDate id="date"
               class="form-control col-sm-8"
               @bind-Value="@Item.Date"
               placeholder="Enter date">
    </InputDate>
</div>

Input Select

Bash.svg
<div class="form-group row">
    <label for="country"
           class="col-sm-3">
        Country: 
    </label>
    <InputSelect id="country"
                 class="form-control col-sm-8"
                 @bind-Value="@CountryId">
        @foreach (var country in Countries)
        {
        <option value="@country.CountryId">@country.Name</option>
        }
    </InputSelect>
</div>

Input CheckBox

Razor.svg
<div class="form-group row">
    <label for="smoker"
           class=" offset-sm-3">
        <InputCheckbox id="smoker"
                       @bind-Value="@Employee.Smoker">
        </InputCheckbox>
        &nbsp;Smoker
    </label>
</div>

Navigation

MyPage.razor
@inject NavigationManager NavigationManager

<a @onclick="@NavigateToHomeAsync">Home</a>

@code {
    private void NavigateToHomeAsync()
    {
        NavigationManager.NavigateTo("home");
    }
}