BlazingPizza: Blazor App von Anfang bis Ende. Teil 2. Fügen Sie eine Komponente hinzu

Hallo an alle! An alle, die etwas mehr über Blazor erfahren möchten. Heute werden wir weiterhin unsere Website für eine Pizzeria erstellen, nämlich einen Web-API-Controller erstellen und versuchen, die daraus resultierenden Daten auf der Blazor-Komponente anzuzeigen.

Da es in unserer Anwendung um Pizza geht, wäre es logisch, sofort eine Klasse hinzuzufügen, die unser Hauptprodukt darstellt. Nennen Sie

es BasePizza und fügen Sie es dem Projekt BlazingPizza.DomainModels hinzu . Meiner Meinung nach ist das Hinzufügen einer neuen Klasse in Rider sehr cool. Ein nicht blockierendes Dialogfeld wird angezeigt. Wir geben den Klassennamen ein und können dann auswählen, was wir erstellen möchten:



Danach erscheint ein Dialogfeld mit der Aufforderung, eine Datei zu git hinzuzufügen. Wir werden dies bejahen.

Klasseninhalt:

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; }
}

Es ist eine Vorlage für eine Art Pizza. Später kann sie nach Belieben konfiguriert werden, die Größe ändern, Beläge hinzufügen und vieles mehr. Der Name der Felder scheint mir für sich zu sprechen.

Im BlazingPizza.DomainPizza- Projekt werden Klassen vorhanden sein, die die Geschäftsdomäne unserer Anwendung darstellen. Das heißt, sie sollten und werden nichts darüber wissen, wie unsere Daten gespeichert sind oder wie sie angezeigt werden. Nur Informationen zum Geschäftsobjekt, dh Pizza.

Als nächstes brauchen wir etwas, um diese Daten irgendwie zum Client zu bringen. Wechseln Sie dazu zum Projekt BlazingPizza.Server und fügen Sie den PizzasController zum Ordner Controller hinzu :

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

Wir brauchen eine Methode, die uns eine Liste aller Grundlagen für Pizza gibt.

Zusätzlich zum Hinzufügen einer Methode müssen Sie einige einfache Schritte ausführen:

  1. Markieren wir den Controller mit dem Attribut [ApiController] , das einige Vorteile bietet, insbesondere die automatische Rückgabe von 400-Code, wenn das Modell die Validierung nicht bestanden hat, ohne dass es sich um einen normalen MVC- Controller handelt, der View bietet.
  2. [Route(«pizzas»)]. Attribute Routing, , Conventional Routing, . “pizzas” ? http{s}://hostName/pizzas/{}
    .
  3. Controller ControllerBase, MVC .

Ok, zum Beispiel haben wir eine localhost : 5000 / pizzas-Anfrage gestellt, in der Hoffnung, eine Liste aller Pizzen zu erhalten, und nichts ist passiert. Auch hier ist der Deal in den Vereinbarungen.

Wenn es sich um eine Get- Anfrage handelte, muss entweder eine Methode ( Aktion in Bezug auf Asp.Net ) mit dem Attribut [HttpGet] markiert sein , oder, noch offensichtlicher, nur eine Methode namens Get und das wars ! Alles andere .Netz und Reflexion werden für uns tun.
Benennen Sie daher die einzige Index-Methode um, die abgerufen werden soll. Ändern Sie den Typ des Rückgabewerts in IEnumerable <BasePizza>Vergessen Sie nicht, die erforderliche Verwendung hinzuzufügen. Fügen Sie vorübergehend einen Stub ein, dessen Methode nicht implementiert ist, um den Code irgendwie zu kompilieren und sicherzustellen, dass keine Fehler vorliegen.

Infolgedessen sieht PizzasController.cs folgendermaßen aus:

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();
        }
    }
}

Starten Sie jetzt die Debugging-Anwendung, eine Schaltfläche mit einem grünen Fehler.



und stellen Sie sicher, dass die Routen korrekt konfiguriert sind. Der Port, an dem Sie Anforderungen stellen müssen, wird auf der Registerkarte " Konsole" angezeigt .



In unserem Fall ist es 5000. Wenn Sie eine Anforderung über den Pfad " localhost : 5000 / pizzas" stellen, rufen wir die Aktion "Get" auf und fangen eine NotImplementedException ab. Das heißt, während unser Controller nichts Nützliches tut, akzeptiert er einfach Anforderungen und schlägt mit einem Fehler fehl.

Wir geben Daten vom Controller zurück


Es ist Zeit, unseren Code dazu zu bringen, etwas Nützliches zu tun, wie beispielsweise die Rückgabe von Pizzen. Bisher haben wir keine Datenschicht implementiert, daher geben wir nur ein paar Pizzen von unserer Aktion zurück . Geben Sie dazu ein Array zurück, das aus zwei BasePizza- Objekten besteht . Die Get- Methode sieht wie im folgenden Beispiel aus:

// 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"
        },
    };
}

Das Ergebnis der Anfrage im Browser sieht folgendermaßen aus:



Richten Sie die Homepage ein


Der sichtbare Teil der Anwendung befindet sich in den .razor-Komponenten im BlazingPizza.Client- Projekt . Wir sind an Index.razor im Ordner Pages interessiert. Öffnen Sie es und löschen Sie alle Inhalte, die wir vom Standardprojekt geerbt haben. Und lassen Sie uns hinzufügen, was wir wirklich brauchen.

1. Hinzufügen:Seite"/" Diese Anweisung wird zum Konfigurieren des Client-Routings verwendet und besagt, dass dieses Steuerelement standardmäßig geladen wird , dh wenn wir nur zur Adresse der localhost- Anwendung gehen : 5000 / without any / Index, / Pizzas oder etwas anderes.

2.injizierenHttpClient HttpClient Verwenden der Richtlinieinjizieren Fügen Sie unserer Seite einen Dienst wie HttpClient hinzu und rufen Sie auch das Objekt HttpClient auf . Ein Objekt vom Typ HttpClient ist bereits vom Blazor-Framework für uns konfiguriert, sodass wir nur die Anforderungen stellen können, die wir benötigen. Diese Art der Injektion wird als Eigenschaftsinjektion bezeichnet . Eine bekanntere Implementierung durch den Konstruktor wird nicht unterstützt. Wie die Entwickler sagen, ist es unwahrscheinlich, dass sie jemals angezeigt wird. Wird sie hier jedoch benötigt?

3. Fügen Sie eine Direktive hinzu

 @code{

 }

Es wird speziell benötigt, um Client-C # -Code zu hosten, der auch JavaScript ersetzt. Innerhalb dieses Blocks platzieren wir eine Sammlung von Objekten vom Typ BasePizzaViewModel

IEnumerable<BasePizzaViewModel> PizzaViewModels;

4. Wie Sie bereits verstanden haben, existiert BasePizzaViewModel nicht. Es ist an der Zeit, es zu erstellen. Dieses Modell ist vollständig analog zum BasePizza- Domänenmodell, mit der Ausnahme, dass es den Ausdruckskörper GetFormattedBasePrice enthält, der den Preis der Basispizza in dem von uns benötigten Format zurückgibt. Das Modell wird zum Stammverzeichnis der BlazingPizza.ViewModels- Datei BasePizzaViewModel.cs des Projekts hinzugefügt :

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. Zurück zu unserem Index.razor und BlockCodeFügen Sie einen Code hinzu, um alle verfügbaren Pizzen zu erhalten. Wir werden diesen Code im platzieren async OnInitializedAsync Methode :

protected async override Task OnInitializedAsync() {
	
}

Diese Methode wird aufgerufen, nachdem die Komponente initialisiert wurde und zum Zeitpunkt des Aufrufs alle ihre Parameter bereits von der übergeordneten Komponente initialisiert wurden. Darin können Sie einige asynchrone Vorgänge ausführen, nach denen eine Statusaktualisierung erforderlich ist. Ich werde später genauer darauf eingehen. Die Methode wird beim Erstellen der Komponente nur einmal aufgerufen.

Fügen Sie zum Schluss die Pizzas hinzu:

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

pizzas - ein relativer Pfad, der der Basis hinzugefügt wird und von Blazor bereits für uns festgelegt wurde . Wie aus der Methodensignatur hervorgeht, werden die Daten von der get- Anforderung angefordert , und der Client versucht dann, sie in IEnumerable <BasePizza> zu serialisieren .

6. Da wir Daten des falschen Typs erhalten haben, die in der Komponente angezeigt werden sollen, müssen wir Objekte vom Typ BasePizzaViewModel abrufen . Linq und seine Select-Methode verwenden sie, um Objekte aus der eingehenden Sammlung in Objekte des Typs zu konvertieren , den wir verwenden möchten . Fügen Sie am Ende die OnInitializedAsync- Methode hinzu :

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

Später werde ich zeigen, wie man ohne das Schreiben dieses Vorlagencodes auskommt, aber jetzt lassen wir es so, wie es ist. Es scheint, dass wir alles haben, was wir brauchen, und wir können fortfahren, die empfangenen Daten anzuzeigen.

7. Fügen Sie über der Code- Direktive den HTML- Code hinzu, in dem sich die Pizzen selbst befinden:

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

    </ul>
</div>

Wie Sie sehen können, ist die Liste ul mit dem Namen der sprechenden Klasse "Pizza-Karten" bisher leer. Wir werden dieses Versehen beheben:

@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>
}

Der ganze Spaß hier ist in der Schleife. für jedes(var {item} in {items})
Dies ist ein typisches Razor- Markup, mit dem wir die Leistung von C # auf derselben Seite wie regulärer HTML- Code verwenden können. Die Hauptsache ist, das "@" -Symbol vor die Schlüsselwörter und Variablen der Sprache zu setzen .

Innerhalb der Schleife greifen wir einfach auf die Eigenschaften des Pizzaobjekts zu .

Am Ende zeigen wir den formatierten Grundpreis der Pizza mit der GetFormattedBasePrice- Methode an . Dies ist übrigens der Unterschied zwischen dem BasePizza- Domänenmodell und seinem ViewModel Darstellungen, da diese Methode die einfachste Logik zur Anzeige des Preises im erforderlichen Format enthält, die wir auf Serviceebene nicht benötigen, wo wir den Preis irgendwie manipulieren, ihn aber nirgendwo anzeigen.

Wir zeigen die empfangenen Daten im Browser an


Wir haben alle notwendigen Daten zur Anzeige. Es ist Zeit, unsere Anwendung zu starten und sicherzustellen, dass alles funktioniert. Wir klicken auf die Schaltfläche Debuggen (in Rider startet die Schaltfläche Ausführen nur die Anwendung ohne Debug- Funktion ).

Und ooh-ho, auch nichts funktioniert :) Öffnen Sie die Konsole ( F12 ) und sehen Sie, dass alles rot ist, etwas ist offensichtlich schief gelaufen. Blazor ist beim Debuggen nicht so hoffnungslos und der gesamte Aufrufstapel kann in der Konsole angezeigt werden , und meiner Meinung nach ist dies sogar noch besser als im selben Winkel . Sie müssen nicht anhand indirekter Anzeichen erraten, wo der Fehler aufgetreten ist. Schauen Sie sich einfach den Aufrufstapel an: Beim



Rendern der Seite ist eine NullReferenceException- Nachricht aufgetreten . Wie könnte dies passieren, weil wir die einzige Sammlung initialisiert haben, die wir in der OnInitializedAsync- Methode verwenden .

Fügen Sie zum besseren Verständnis die Zeitausgabe an den richtigen Stellen ein, um den Zeitrahmen des Geschehens anzuzeigen:

  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()}"); 



Im folgenden Screenshot in der Konsole, was zum Zeitpunkt des Renderns der Seite passiert ist. Es ist ersichtlich, dass die Seite bereits vor Abschluss der asynchronen Methoden gerendert wird.
Beim ersten Durchgang wurde PizzaViewModels noch nicht initialisiert und wir haben eine NullReferenceException abgefangen . Nachdem die Task die OnInitializedAsync- Methode in den RanToCompletion- Status zurückversetzt hatte, wurde das Steuerelement erwartungsgemäß neu entwickelt. Was bemerkenswert ist, während des zweiten Durchgangs sind wir in einen Zyklus geraten, wie aus den Nachrichten in der Konsole ersichtlich ist. Zu diesem Zeitpunkt wird die Benutzeroberfläche jedoch nicht mehr aktualisiert und es werden keine sichtbaren Änderungen angezeigt.



Tatsächlich ist das Problem sehr einfach zu lösen. Sie müssen lediglich eine Nullprüfung asynchron ausführen, bevor Sie die Schleife ausführen . Dann tritt zum ersten Mal keine Ausnahme auf, und während des zweiten Durchlaufs werden die benötigten Daten angezeigt.
@if (PizzaViewModels != null)
{
    @foreach (var pizza in PizzaViewModels)
    {
        ……………………….. //    
    }
}


Es scheint jetzt ein bisschen besser zu sein, es gibt keine Fehlermeldungen mehr in der Konsole und Sie können die Informationen sehen, die vom Server zu uns gekommen sind:



Es ist viel besser, aber es gibt nicht genügend Stile und Ressourcen, insbesondere Bilder. Ersetzen Sie den Inhalt des Ordners wwwroot durch den Inhalt des Ordners "~ / Articles / Part2" /BlazingPizza.Client/wwwroot "Repository (Link am Ende des Artikels) und führen Sie das Projekt erneut aus, viel besser. Obwohl immer noch alles andere als ideal:



Komponentenlebensereignisse


Da wir bereits eines der Lebensereignisse der OnInitializedAsync-Komponente getroffen haben, wäre es logisch, die anderen zu erwähnen:
Initialisierungsmethoden
Oninitialisiert
Wird aufgerufen, wenn die Komponente bereits initialisiert ist und ihre Parameter bereits von der übergeordneten Komponente festgelegt wurden. Während der Lebensdauer einer Komponente wird sie nach ihrer Initialisierung einmal aufgerufen.
OnInitializedAsync
Asynchrone Version der ersten Methode Nach der Ausführung wird die Komponente erneut gerendert. Daher müssen Sie beim Schreiben von Code berücksichtigen, dass einige Objekte möglicherweise null sind.
Ausführbare Methode vor dem Festlegen von Parameterwerten
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


In diesem Teil haben wir gelernt, wie Daten vom Controller empfangen und dem Benutzer angezeigt werden.
Im nächsten Teil werden wir das Layout aufräumen und eine Datenzugriffsebene hinzufügen, um echte Daten auf der Hauptseite anzuzeigen.

Link zum Repository dieser Artikelserie.
Link zur Originalquelle.

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


All Articles