Blazor : Simple CRUD App Component, Component, Component

Update v0.2

Série d'article sur Blazor :

Dans le précédent article, on a vu comment créer un service qui appele une WebAPI. Le but de ce nouvel article est d'assimiler la notion de component (composant in french) qui est au centre de Blazor et qui, si on le comprend bien, permettra d'optimiser les performances de notre application.

Tout d'abord, il faut comprendre comment fonctionne le moteur de mise à jour du DOM par Blazor.

  • Une page est un Component.
  • Une page peut contenir plusieurs Component.
  • Chaque Component est responsable de sa mise à jour.

Le moteur de Blazor est suffisament intelligent pour ne mettre à jour que component par component.

Donc si vous avez une page qui comprend 15 components et que vous ne modifiez qu'un seul component, Blazor ne mettra à jour le DOM de la page du navigateur que pour le fragment du component en question.

Prenons l'exemple d'une grille, il est sans doute judicieux de définir chaque ligne comme état un component. Ainsi, si vous mettez à jour une ligne, seule cette ligne sera mise à jour dans le DOM par Blazor.

Mettons cela en pratique.

Dans mon projet, j'ai crée un dossier PersonsComponent avec un PersonGrid et un PersonRow :

Visuellement parlant, cela va se traduire ainsi :

La zone rouge correspond à mon composant PersonsGrid qui contient une collection de PersonRow (en vert). Quand je vais cliquer sur Edit ou Delete, seul le composant PersonRow correspondant sera affecté.

PersonGrid

Regardons le code de PersonsGrid :

@using System.Collections.Generic
@using System.Threading.Tasks
@using SimpleCrudBlazor.Client.Services;
@using SimpleCrudBlazor.Shared;
@inject PersonService _personService
 
@if (persons != null)
{
    @foreach (var person in persons)
    {
        <PersonRow Person="person" OnIsSelectedChanged="OnIsSelectedChanged" OnUpdate="OnUpdateAsync"></PersonRow>
    }
}
 
@functions {
    private IEnumerable<Person> persons;
    private PersonRow _selectedPersonRow;
 
    protected override async Task OnInitAsync()
    {
        await RefreshAsync();
    }
 
    /// <summary>
    /// Charge les personnes
    /// </summary>
    async Task RefreshAsync()
    {
        persons = await _personService.GetAllAsync();
    }
 
    /// <summary>
    /// Passe en mode édition une personne
    /// </summary>
    /// <param name="personRow">La personne a mettre en mmode édition</param>
    void OnIsSelectedChanged(PersonRow personRow)
    {
        if (_selectedPersonRow == personRow) return;
 
        if (_selectedPersonRow != null)
            _selectedPersonRow.IsSelected = false;
        _selectedPersonRow = personRow;
    }
 
    /// <summary>
    /// Met à jour une personne
    /// </summary>
    async void OnUpdateAsync(PersonRow personRow)
    {
        var person = await _personService.UpdateAsync(personRow.Person);
        personRow.Person = person;
        personRow.NotifyStateHasChanged();
    }
}
 

J'ai une propriété _persons qui contient la liste de mes personnes que je récupère via mon service PersonService et la méthode RefreshAsync.

J'ai aussi une propriété _selectedPersonRow qui m'indique quelle est la ligne sélectionnée, qui est en mode édition (il ne peut y en avoir qu'une).

Pour chaque person de mes _persons, je crée un component de type PersonRow qui a 2 propriétés de type Action<PersonRow>:

  • OnIsSelectedChanged qui m'indique que la ligne a été sélectionnée,
  • OnUpdate qui m'indique que l'on a cliqué sur le bouton Update de la ligne.

NB : je n'ai pas encore implémenté la méthode Delete.

Quand la méthode OnIsSelectedChanged est appelée, je met à jour le champ _selectedPersonRow pour garder la trace de la ligne sélectionnée. Si il y avait déjà une ligne sélectionnée, j'affecte sa propriété IsSelected à false.

Quand la méthode OnUpdate est appelée, j'appele le service PersonService pour mettre à jour la personne dans la base de donnée sur le serveur. Et surtout, je demande au composant de la ligne (le PersonRow) de se mettre à jour avec NotifyStateHasChanged.

PersonRow

Côté PersonRow, c'est encore plus simple :

@using System
@using SimpleCrudBlazor.Shared
@using SimpleCrudBlazor.Client.Services;
@if (_isSelected)
{
    <div class="row">
        <div class="col-xs-1"><button onclick="(() => OnUpdatePerson())">Update</button></div>
        <div class="col-xs-1"><button onclick="(() => OnToggleEditMode())">Cancel</button></div>
        <div class="col-xs-5"><input bind="@Person.FirstName" type="text"></div>
        <div class="col-xs-5"><input bind="@Person.LastName" type="text"></div>
    </div>
}
else
{
    <div class="row">
        <div class="col-xs-1"><button onclick="@(() => OnToggleEditMode())">Edit</button></div>
        <div class="col-xs-1"><button onclick="@(()=> OnDelete())">Delete</button></div>
        <div class="col-xs-5">@Person.FirstName</div>
        <div class="col-xs-5">@Person.LastName</div>
    </div>
}
 
@functions {
    private bool _isSelected;
    public Person Person {get;set;}
    public Action<PersonRow> OnIsSelectedChanged { getset; }
    public Action<PersonRow> OnUpdate { getset; }
 
    public bool IsSelected
    {
        get => _isSelected;
        set
        {
            if (IsSelected == valuereturn;
 
            _isSelected = value;
            OnIsSelectedChanged(this);
            this.StateHasChanged();
        }
    }
 
    public void NotifyStateHasChanged()
    {
        this.StateHasChanged();
    }
 
    void OnToggleEditMode()
    {
        IsSelected = !IsSelected;
    }
 
    void OnDelete()
    {
        // TODO
    }
 
    void OnUpdatePerson()
    {
        OnUpdate(this);
        IsSelected = false;
    }
}
 

Je passe sur le contenu de l'UI mais vous voyez que quand on clique sur le bouton Update par exemple, la méthode OnUpdatePerson est appelée, et cette dernière appele l'Action<PersonRow> OnUpdate (cf le PersonsGrid).

Vous remarquerez que dans la propriété IsSelected, j'appele la méthode StateHasChanged : c'est pour indiquer au moteur Blazor que l'UI de ce composant doit être mis à jour. Comme cette méthode est protected, je l'expose via la méthode NotifyStateHasChanged qui est appelée par le PersonsGrid quand un update est fait.

Voilà!

Simple mais il est très important avec Balzor dez bien comprendre que tout est Component pour encore optimiser les performances de votre application.

blog comments powered by Disqus