« Blazor .NET Core 3.1 » : différence entre les versions
De Banane Atomic
Aller à la navigationAller à la recherche
Ligne 158 : | Ligne 158 : | ||
Task<Item> GetItemAsync(int itemId); | Task<Item> GetItemAsync(int itemId); | ||
Task UpdateItemAsync(Item item); | Task UpdateItemAsync(Item item); | ||
Task DeleteItemAsync(int itemId); | |||
} | } | ||
</filebox> | </filebox> | ||
Ligne 200 : | Ligne 201 : | ||
await _httpClient.PutAsync("api/item", itemJson); | await _httpClient.PutAsync("api/item", itemJson); | ||
} | |||
public async Task DeleteItemAsync(int itemId) | |||
{ | |||
await _httpClient.DeleteAsync($"item/{itemId}"); | |||
} | } | ||
</filebox> | </filebox> |
Version du 17 janvier 2020 à 14:28
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
# 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 "/items" @* si c'est une page, son url avec un parameter *@ @page "/item/{Id}" @* attendre que la valeur soit chargée *@ @if (Items == null) { <p><em>Loading...</em></p> } else { /* ... */ } @code { [Parameter] public string ItemId { 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.cs |
public interface IItemDataService { Task<IEnumerable<Item>> GetAllItemsAsync(); Task<Item> GetItemAsync(int itemId); Task UpdateItemAsync(Item item); Task DeleteItemAsync(int itemId); } |
Services/ItemDataService.cs |
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 }); } public async Task<Item> GetItemAsync(int itemId) { return await JsonSerializer.DeserializeAsync<Item>( await _httpClient.GetStreamAsync($"api/item/{itemId}"), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); } public async Task UpdateItemAsync(Item item) { var itemJson = new StringContent( JsonSerializer.Serialize(item), Encoding.UTF8, "application/json"); await _httpClient.PutAsync("api/item", itemJson); } public async Task DeleteItemAsync(int itemId) { await _httpClient.DeleteAsync($"item/{itemId}"); } |
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
EditItem.razor |
@page "/edititem/{ItemId}" @* to access to Model.Item *@ @using Model @inherits EditItemBase <EditForm Model="@Item" OnValidSubmit="@HandleValidSubmitAsync" OnInvalidSubmit="@HandleInvalidSubmitAsync"> <button type="submit">Save</button> <button @onclick="Cancel">Cancel</button> </EditForm> |
EditItemBase.cs |
[Parameter] public string ItemId { get; set; } [Inject] public IItemDataService ItemDataService { get; set; } public Item Item { get; set; } = new Item(); protected override async Task OnInitializedAsync() { int.TryParse(ItemId, out var itemId); Item = await ItemDataService.GetItemAsync(int.Parse(ItemId)); } protected async Task HandleValidSubmitAsync() { await ItemDataService.UpdateItemAsync(Item); } protected async Task HandleInvalidSubmitAsync() { } |
Input Text
<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> </div> |
Input Text Area
<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> </div> |
Input Number
<div class="form-group row"> <label for="price" class="col-sm-3">Price: </label> <InputNumber id="price" @bind-Value="@Item.Price" class="form-control col-sm-8"> </InputNumber> </div> |
Input Date
<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
<div class="form-group row"> <label for="country" class="col-sm-3">Color: </label> <InputSelect id="color" @bind-Value="@CountryId" class="form-control col-sm-8"> @foreach (var color in Enum.GetValues(typeof(RgbColor))) { <option value="@color">@color</option> } <option value="@RgbColor.Red">@RgbColor.Red</option> <option value="@RgbColor.Green">@RgbColor.Green</option> <option value="@RgbColor.Blue">@RgbColor.Blue</option> </InputSelect> </div> |
Input CheckBox
<div class="form-group row"> <label for="smoker" class=" offset-sm-3"> <InputCheckbox id="smoker" @bind-Value="@Employee.Smoker"> </InputCheckbox> Smoker </label> </div> |
Form validation
Item.cs |
// nécessite le package nuget System.ComponentModel.Annotations [Required] [StringLength(50, ErrorMessage = "Name too long (max 50 char)")] public string Name { get; set; } |
Pages/ItemEdit.razor |
<EditForm Model="@Item"> <DataAnnotationsValidator /> @* affiche la liste des messages d'erreur de validation *@ <ValidationSummary /> <div class="form-group row"> <label for="name" class="col-sm-3">Name: </label> <InputText id="name" @bind-value="@Item.Name" class="form-control col-sm-8" placeholder="Enter name"></InputText>> @* affiche le message d'erreur de validation *@ <ValidationMessage class="offset-sm-3 col-sm-8" For="@(() => Item.Name)" /> |
MyPage.razor |
@inject NavigationManager NavigationManager <a @onclick="@NavigateToHome">Home</a> @code { private void NavigateToHome() { NavigationManager.NavigateTo("home"); } } |