BlazingPizza: application Blazor du début à la fin. Partie 2. Ajouter un composant

Bonjour tous le monde! À tous ceux qui veulent en savoir un peu plus sur Blazor. Aujourd'hui, nous continuerons à créer notre site pour une pizzeria, à savoir, nous allons créer un contrôleur web api et essayer d'afficher les données qui en découlent sur le composant Blazor.

Étant donné que notre application concerne la pizza, il serait logique d'ajouter immédiatement une classe représentant notre produit principal. Appelez-

le BasePizza et ajoutez-le au projet BlazingPizza.DomainModels . À mon avis, l'ajout d'une nouvelle classe est très cool implémenté dans Rider, une boîte de dialogue non bloquante apparaît, nous entrons le nom de la classe et nous pouvons ensuite choisir ce que nous devons créer:



Après cela, une boîte de dialogue apparaîtra avec une demande d'ajout d'un fichier à git, nous vous répondrons par l'affirmative.

Contenu du cours:

public class BasePizza
{
  public int Id { get; set; }
    
    public string Name { get; set; }
    
    public decimal BasePrice { get; set; }
    
    public string Description { get; set; }
    
    public string ImageUrl { get; set; }
}

C'est un modèle pour certains types de pizza, plus tard, il peut être configuré comme nous le souhaitons, redimensionner, ajouter des garnitures et plus encore. Le nom des champs me semble parler de lui-même.

Dans le projet BlazingPizza.DomainPizza , nous aurons des classes représentant le domaine d'activité de notre application. Autrement dit, ils ne devraient pas savoir et ne sauront rien sur la façon dont nos données sont stockées ou comment elles sont affichées. Seules des informations sur l'objet métier, c'est-à-dire la pizza.

Ensuite, nous avons besoin de quelque chose pour transmettre ces données au client. Pour ce faire, accédez au projet BlazingPizza.Server et ajoutez le PizzasController au dossier Contrôleurs :

public class PizzasController : Controller
{
    // GET
    public IActionResult Index()
    {
        return View();
    }
}

Nous avons besoin d'une méthode qui nous donne une liste de toutes les bases de la pizza.

En plus d'ajouter une méthode, vous devez effectuer quelques étapes simples:

  1. Marquons le contrôleur avec l'attribut [ApiController] qui donne certains avantages, notamment le retour automatique de 400 code si le modèle n'a pas passé la validation, sans lui c'est un contrôleur MVC normal qui donne View.
  2. [Route(«pizzas»)]. Attribute Routing, , Conventional Routing, . “pizzas” ? http{s}://hostName/pizzas/{}
    .
  3. Controller ControllerBase, MVC .

Ok, par exemple, nous avons fait une demande localhost : 5000 / pizzas dans l'espoir d'obtenir une liste de toutes les pizzas et rien ne s'est passé. Encore une fois, l'accord est dans les accords.

S'il s'agissait d'une demande Get , alors nous devons soit avoir une méthode ( Action en termes d' Asp.Net ) marquée avec l'attribut [HttpGet] ou, encore plus évident, juste une méthode appelée Get et c'est tout! Tout le reste .Net et la réflexion feront pour nous.
Et renommez ainsi la seule méthode Index en Get. Remplacez le type de la valeur de retour par IEnumerable <BasePizza>, n'oubliez pas d'ajouter le nécessaire en utilisant. Eh bien, insérez temporairement un stub que la méthode n'est pas implémentée afin de compiler le code et assurez-vous qu'il n'y a pas d'erreurs.

Par conséquent, PizzasController.cs ressemblera à ceci:

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using BlazingPizza.DomainModels;

namespace BlazingPizza.Server.Controllers
{
    [ApiController]
    [Route("pizzas")]
    public class PizzasController : ControllerBase
    {
        // GET
        public IEnumerable<BasePizza>  Get()
        {
            throw new NotImplementedException();
        }
    }
}

Pour le moment, lancez l'application de débogage, un bouton avec un bug vert.



et assurez-vous que les itinéraires sont correctement configurés. Le port sur lequel vous devez effectuer des requêtes est visible sur l'onglet Console :



dans notre cas, il s'agit de 5000, si vous effectuez une requĂŞte le long du chemin localhost : 5000 / pizzas, nous entrons dans l'action Get et capturons une exception NotImplementedException. Autrement dit, alors que notre contrĂ´leur ne fait rien d'utile, il accepte simplement les demandes et Ă©choue avec une erreur.

Nous renvoyons les données du contrôleur


Il est temps que notre code fasse quelque chose d'utile, comme retourner des pizzas. Jusqu'à présent, nous n'avons pas mis en place de couche de données, nous retournons donc simplement quelques pizzas de notre action . Pour ce faire, renvoyez un tableau composé de deux objets BasePizza . La méthode Get ressemblera à l'exemple ci-dessous:

// GET
public IEnumerable<BasePizza>  Get()
{
    return new[]
    {
        new BasePizza()
        {
            BasePrice = 500,
            Description = "     ",
            Id = 0,
            ImageUrl = "img/pizzas/pepperoni.jpg"
        },
        new BasePizza()
        {
            BasePrice = 400,
            Description = "   ",
            Id = 1,
            ImageUrl = "img/pizzas/meaty.jpg"
        },
    };
}

Le résultat de la demande dans le navigateur sera le suivant:



Configurer la page d'accueil


La partie visible de l'application se trouve dans les composants .razor du projet BlazingPizza.Client . Nous sommes intéressés par Index.razor dans le dossier Pages , ouvrez-le et supprimez tout son contenu que nous avons hérité du projet par défaut. Et commençons à ajouter ce dont nous avons vraiment besoin.

1. Ajoutez:page"/" Cette directive est utilisée pour configurer le routage client et indique que ce contrôle sera chargé par défaut, c'est-à-dire si nous allons simplement à l'adresse de l'application localhost : 5000 / sans aucun / Index, / Pizzas ou autre chose.

2.injecterHttpClient HttpClient utilisant la directiveinjecter ajoutez un service comme HttpClient à notre page et appelez également l'objet HttpClient . Un objet de type HttpClient est déjà configuré pour nous par le framework Blazor, nous pouvons donc simplement faire les requêtes dont nous avons besoin. Ce type d'injection s'appelle Property Injection , une implémentation plus familière via le constructeur n'est pas prise en charge, et comme le disent les développeurs, il est peu probable qu'elle apparaisse un jour, mais est-elle nécessaire ici?

3. Ajoutez une directive

 @code{

 }

Il est spécifiquement nécessaire pour héberger le code client C #, le même qui remplace JavaScript. À l'intérieur de ce bloc, nous plaçons une collection d'objets de type BasePizzaViewModel

IEnumerable<BasePizzaViewModel> PizzaViewModels;

4. Comme vous l'avez déjà compris, BasePizzaViewModel n'existe pas, il est temps de le créer, ce modèle sera complètement analogue au modèle de domaine BasePizza , sauf qu'il aura le corps d'expression GetFormattedBasePrice qui retourne le prix de la pizza de base dans le format dont nous avons besoin. Le modèle s'ajoutera à la racine du projet BlazingPizza.ViewModels fichier BasePizzaViewModel.cs :

public class BasePizzaViewModel
{
    public int Id { get; set; }
    
    public string Name { get; set; }
    
    public decimal BasePrice { get; set; }
    
    public string Description { get; set; }
    
    public string ImageUrl { get; set; }
    
    public string GetFormattedBasePrice() => BasePrice.ToString("0.00");
}

5. Retour à notre Index.razor et bloccode, ajoutez un code pour obtenir toutes les pizzas disponibles. Nous placerons ce code dans la méthode async OnInitializedAsync :

protected async override Task OnInitializedAsync() {
	
}

Cette méthode est appelée après l'initialisation du composant et au moment de l'appel, tous ses paramètres sont déjà initialisés par le composant parent. Dans ce document, vous pouvez effectuer certaines opérations asynchrones, après quoi une mise à jour d'état est requise. Plus tard, j'en parlerai plus en détail. La méthode n'est appelée qu'une seule fois lors de la création du composant.

Enfin, ajoutez les pizzas à obtenir dans cette méthode:

var queryResult = await HttpClient.GetJsonAsync<IEnumerable<BasePizza>>("pizzas");

pizzas - un chemin relatif qui est ajouté à la base et qui a déjà été défini pour nous par Blazor . Comme suit à partir de la signature de la méthode, les données sont demandées par la demande get , puis le client essaie de les sérialiser en IEnumerable <BasePizza> .

6. Puisque nous avons reçu des données du mauvais type que nous voulons afficher dans le composant, nous devons obtenir des objets de type BasePizzaViewModel , Linq et sa méthode Select les utiliseront pour convertir les objets de la collection entrante en objets du type que nous prévoyons d'utiliser. Ajoutez la méthode OnInitializedAsync à la fin :

PizzaViewModels = queryResult.Select(i => new BasePizzaViewModel()
{
    BasePrice = i.BasePrice,
    Description = i.Description,
    Id = i.Id,
    ImageUrl = i.ImageUrl,
    Name = i.Name
});

Plus tard, je montrerai comment faire sans écrire ce code de modèle, mais pour l'instant, laissons-le tel quel. Il semble que nous ayons tout ce dont nous avons besoin et nous pouvons procéder à l'affichage des données reçues.

7. Au-dessus de la directive code , ajoutez le code html , à l'intérieur duquel se trouveront les pizzas elles-mêmes:

<div class="main">
    <ul class="pizza-cards">

    </ul>
</div>

Comme vous pouvez le voir, la liste ul avec le nom de la classe parlante «pizza-cards» est vide jusqu'à présent, nous allons corriger cette erreur:

@foreach (var pizza in PizzaViewModels)
{
    <li style="background-image: url('@pizza.ImageUrl')">
        <div class="pizza-info">
            <span class="title">@pizza.Name</span>
                @pizza.Description
            <span class="price">@pizza.GetFormattedBasePrice()</span>                    
        </div>
    </li>
}

Tout le plaisir ici est à l'intérieur de la boucle. pour chaque(var {item} in {items})
Il s'agit d'un balisage Razor typique qui nous permet d'utiliser la puissance de C # sur la même page que le code html normal . L'essentiel est de placer le symbole «@» devant les mots clés et les variables de la langue .

À l'intérieur de la boucle, nous accédons simplement aux propriétés de l'objet pizza .

Au final, nous affichons le prix de base formaté de la pizza à l'aide de la méthode GetFormattedBasePrice . C'est d'ailleurs la différence entre le modèle de domaine BasePizza et son ViewModel représentations, car cette méthode contient la logique la plus simple pour afficher le prix dans le format requis, dont nous n'avons pas besoin au niveau du service, où nous manipulons en quelque sorte le prix, mais ne le montrons nulle part.

Nous affichons les données reçues dans le navigateur


Nous avons obtenu toutes les données nécessaires à afficher. Il est temps de lancer notre application et de vous assurer que tout fonctionne. Nous cliquons sur le bouton Déboguer (dans Rider, le bouton Exécuter lance simplement l'application sans capacité de débogage ).

Et ooh-ho, rien ne fonctionne non plus :) Ouvrez la console ( F12 ) et voyez que tout est rouge, quelque chose s'est évidemment mal passé. Blazor n'est pas si désespéré dans le débogage et toute la pile d'appels peut être vue dans la console , et à mon avis, cela se fait encore mieux que dans le même Angular . Pas besoin de deviner par des signes indirects où l'erreur s'est produite, il suffit de regarder la pile des appels:



Un message NullReferenceException s'est produit lors du rendu de la page . Comment cela peut-il arriver, car nous avons initialisé la seule collection que nous utilisons dans la méthode OnInitializedAsync .

Pour mieux comprendre, insérez la sortie de temps aux bons endroits pour voir le calendrier de ce qui s'est passé:

  1. Console.WriteLine($"Time from markup block: {DateTime.Now.ToString()}:{DateTime.Now.Millisecond.ToString()}");
  2. Console.WriteLine($"Time from cycle: {DateTime.Now.ToString()}:{DateTime.Now.Millisecond.ToString()}");
  3. Console.WriteLine($"Time from code block, before await: {DateTime.Now.ToString()}:{DateTime.Now.Millisecond.ToString()}");
  4. Console.WriteLine($"Time from code block, after await: {DateTime.Now.ToString()}:{DateTime.Now.Millisecond.ToString()}"); 



Dans la capture d'écran ci-dessous, dans la console, ce qui s'est passé au moment du rendu de la page. On peut voir que la page commence à s'afficher avant même la fin des méthodes asynchrones.
Lors de la première passe, PizzaViewModels n'était pas encore initialisé et nous avons détecté une exception NullReferenceException . Ensuite, comme prévu après que la tâche a renvoyé la méthode OnInitializedAsync à l' état RanToCompletion , le contrôle a été repensé . Ce qui est remarquable, lors de la deuxième passe, nous sommes entrés dans un cycle, comme le montrent les messages dans la console. Mais à ce stade, l' interface utilisateur n'est plus mise à jour et nous ne voyons aucun changement visible.



En fait, le problème est très facile à résoudre, il vous suffit d'exécuter une vérification nulle de manière asynchrone avant d'exécuter la boucle , puis l'exception ne se produira pas pour la première fois et lors de la deuxième passe, nous verrons les données dont nous avons besoin.
@if (PizzaViewModels != null)
{
    @foreach (var pizza in PizzaViewModels)
    {
        ……………………….. //    
    }
}


Cela semble un peu mieux maintenant, il n'y a plus de messages d'erreur dans la console et vous pouvez voir les informations qui nous sont parvenues du serveur:



c'est beaucoup mieux, mais il n'y a pas assez de styles et de ressources, en particulier des images, remplacez le contenu du dossier wwwroot par le contenu du dossier "~ / Articles / Part2 /BlazingPizza.Client/wwwroot "(lien à la fin de l'article) et relancez le projet, tant mieux. Bien que loin d'être idéal:



Événements de la vie des composants


Puisque nous avons déjà rencontré l'un des événements de la vie du composant OnInitializedAsync, il serait logique de mentionner les autres:
MĂ©thodes d'initialisation
Oninitialized
Appelé lorsque le composant est déjà initialisé et que ses paramètres sont déjà définis par le composant parent. Pendant la durée de vie d'un composant, il est appelé une fois après son initialisation.
OnInitializedAsync
Version asynchrone de la première méthode, après exécution, le composant est restitué. Par conséquent, lors de l'écriture de code, vous devez tenir compte du fait que certains objets peuvent être nuls.
Méthode exécutable avant de définir les valeurs des paramètres
SetParametersAsync
, . ParameterView .

[Parameter] [CascadingParameter] ParameterView. , . , )

OnParametersSet
. ,

— .
— , .
OnParametersSetAsyncOnParametersSet
OnAfterRender. . JavaScript DOM . bool firstRender true .
OnAfterRenderAsync
, Task , - .

ShouldRender
UI, - . . .
StateHasChanged
, Blazor .
Dispose
, UI. StateHasChanged Dispose . Dispose IDisposable, @implements IDisposable


Dans cette partie, nous avons appris à recevoir des données du contrôleur et à les afficher à l'utilisateur.
Dans la partie suivante, nous allons ranger la disposition et ajouter une couche d'accès aux données pour afficher les données réelles sur la page principale.

Lien vers le référentiel de cette série d'articles.
Lien vers la source d'origine.

Source: https://habr.com/ru/post/undefined/


All Articles