BlazingPizza: Aplikasi Blazor dari awal hingga akhir. Bagian 2. Tambahkan komponen

Halo semua! Kepada semua yang ingin belajar lebih banyak tentang Blazor. Hari ini kami akan terus membuat situs kami untuk pizzeria, yaitu, kami akan membuat pengontrol api web dan mencoba menampilkan data yang berasal darinya pada komponen Blazor.

Karena aplikasi kita adalah tentang pizza, masuk akal untuk segera menambahkan kelas yang mewakili produk utama kami. Sebut

saja BasePizza dan tambahkan ke proyek BlazingPizza.DomainModels . Menurut pendapat saya, menambahkan kelas baru sangat keren diimplementasikan di Rider, dialog non-pemblokiran muncul, kita masukkan nama kelas dan kemudian kita dapat memilih apa yang perlu kita buat:



Setelah itu, sebuah dialog akan muncul dengan permintaan untuk menambahkan file ke git, kami akan menjawab dalam afirmatif.

Konten Kelas:

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

Ini adalah templat untuk beberapa jenis pizza, nanti bisa dikonfigurasikan sesuka kita, ubah ukuran, tambahkan topping, dan lainnya. Bagi saya, nama ladang itu berbicara sendiri.

Dalam proyek BlazingPizza.DomainPizza , kita akan memiliki kelas yang mewakili domain bisnis aplikasi kita. Artinya, mereka seharusnya tidak dan tidak akan tahu apa-apa tentang bagaimana data kita disimpan atau bagaimana mereka ditampilkan. Hanya informasi tentang objek bisnis, yaitu pizza.

Selanjutnya, kita perlu sesuatu untuk mendapatkan data ini entah bagaimana kepada klien. Untuk melakukan ini, buka proyek BlazingPizza.Server dan tambahkan PizzasController ke folder Controllers :

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

Kami membutuhkan metode yang memberi kami daftar semua dasar untuk pizza.

Selain menambahkan metode, Anda perlu melakukan beberapa langkah sederhana:

  1. Mari kita tandai controller dengan atribut [ApiController] yang memberikan beberapa keuntungan, khususnya, pengembalian otomatis 400 kode jika model tidak lulus validasi, tanpa itu controller MVC normal yang memberikan View.
  2. [Route(ยซpizzasยป)]. Attribute Routing, , Conventional Routing, . โ€œpizzasโ€ ? http{s}://hostName/pizzas/{}
    .
  3. Controller ControllerBase, MVC .

Ok, misalnya, kita membuat localhost : 5000 / pizza meminta dengan harapan mendapatkan daftar semua pizza dan tidak ada yang terjadi. Sekali lagi, kesepakatan ada dalam perjanjian.

Jika itu adalah permintaan Dapatkan , maka kita harus memiliki metode ( Tindakan dalam hal Asp.Net ) yang ditandai dengan atribut [HttpGet] , atau, yang lebih jelas, hanya metode yang disebut Dapatkan dan hanya itu! Segala sesuatu yang lain. Net dan refleksi akan bermanfaat bagi kita.
Dan ganti nama satu-satunya metode Indeks untuk Dapatkan. Ubah jenis nilai kembali ke IEnumerable <BasePizza>, jangan lupa tambahkan yang perlu pakai. Baiklah, untuk sementara masukkan sebuah rintisan bahwa metode ini tidak diimplementasikan untuk mengkompilasi kode dan memastikan bahwa tidak ada kesalahan.

Akibatnya, PizzasController.cs akan terlihat seperti ini:

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

Sekarang, luncurkan aplikasi debugging, sebuah tombol dengan bug hijau.



dan pastikan rute dikonfigurasi dengan benar. Port tempat Anda perlu membuat permintaan dapat dilihat pada tab Konsol :



dalam kasus kami, itu adalah 5000, jika Anda membuat permintaan di sepanjang localhost : jalur 5000 / pizza, maka kami masuk ke tindakan Dapatkan dan menangkap NotImplementedException. Yaitu, sementara pengontrol kami tidak berguna, ia hanya menerima permintaan dan gagal dengan kesalahan.

Kami mengembalikan data dari pengontrol


Saatnya mendapatkan kode kami untuk melakukan sesuatu yang bermanfaat, seperti mengembalikan pizza. Sejauh ini, kami belum menerapkan lapisan data, jadi kami hanya mengembalikan beberapa pizza dari tindakan kami . Untuk melakukan ini, kembalikan array yang terdiri dari dua objek BasePizza . Metode Get akan terlihat seperti contoh di bawah ini:

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

Hasil dari permintaan di browser akan seperti ini:



Siapkan beranda


Bagian yang terlihat dari aplikasi adalah komponen .razor dalam proyek BlazingPizza.Client . Kami tertarik dengan Index.razor di folder Halaman , buka dan hapus semua kontennya yang kami warisi dari proyek default. Dan mari kita mulai menambahkan apa yang benar-benar kita butuhkan.

1. Tambahkan:halaman"/" Arahan ini digunakan untuk mengonfigurasi perutean klien dan mengatakan bahwa kontrol ini akan dimuat secara default, yaitu, jika kita hanya pergi ke alamat aplikasi localhost : 5000 / tanpa / Indeks, / Pizza atau yang lainnya.

2.menyuntikkanHttpClient HttpClient Menggunakan Petunjukmenyuntikkan tambahkan layanan seperti HttpClient ke halaman kami dan panggil objek HttpClient juga . Objek tipe HttpClient sudah dikonfigurasi untuk kita oleh kerangka kerja Blazor, jadi kita bisa membuat permintaan yang kita butuhkan. Jenis injeksi ini disebut Injeksi Properti , implementasi yang lebih akrab melalui konstruktor tidak didukung, dan seperti yang dikatakan pengembang, tidak mungkin itu akan pernah muncul, tetapi apakah ini diperlukan di sini?

3. Tambahkan arahan

 @code{

 }

Ini diperlukan secara khusus untuk meng-host kode C # klien, kode yang sama yang merupakan pengganti JavaScript. Di dalam blok ini kita menempatkan koleksi objek bertipe BasePizzaViewModel

IEnumerable<BasePizzaViewModel> PizzaViewModels;

4. Seperti yang sudah Anda pahami, BasePizzaViewModel tidak ada, saatnya untuk membuatnya, model ini akan sepenuhnya analog dengan model domain BasePizza , kecuali bahwa ia akan memiliki tubuh ekspresi GetFormattedBasePrice yang mengembalikan harga pizza basis dalam format yang kami butuhkan. Model ini akan menambah akar proyek BlazingPizza.ViewModels berkas 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. Kembali ke Index.razor kami dan blokirkode, tambahkan kode untuk mendapatkan semua pizza yang tersedia. Kami akan menempatkan kode ini dalam metode OnInitializedAsync async :

protected async override Task OnInitializedAsync() {
	
}

Metode ini dipanggil setelah komponen diinisialisasi dan pada saat panggilan, semua parameternya sudah diinisialisasi oleh komponen induk. Di dalamnya, Anda dapat melakukan beberapa operasi asinkron, setelah itu pembaruan keadaan diperlukan. Nanti saya akan membicarakan hal ini secara lebih detail. Metode ini dipanggil hanya sekali ketika komponen dibuat.

Akhirnya, tambahkan pizza di dalam metode ini:

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

pizza - jalur relatif yang ditambahkan ke pangkalan dan telah ditetapkan untuk kita oleh Blazor . Sebagai berikut dari tanda tangan metode, data diminta oleh permintaan get dan kemudian klien mencoba untuk membuat serial mereka ke dalam IEnumerable <BasePizza> .

6. Karena kami menerima data dari tipe yang salah yang ingin kami tampilkan dalam komponen, kami perlu mendapatkan objek tipe BasePizzaViewModel , Linq dan metode Pilihnya akan menggunakannya untuk mengonversi objek dari koleksi yang masuk ke objek dari jenis yang akan kami gunakan. Tambahkan metode OnInitializedAsync ke akhir :

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

Nanti saya akan menunjukkan bagaimana melakukannya tanpa menulis kode templat ini, tetapi untuk sekarang, mari kita biarkan apa adanya. Tampaknya kami memiliki semua yang kami butuhkan dan kami dapat melanjutkan untuk menampilkan data yang diterima.

7. Di atas arahan kode , tambahkan kode html , di dalamnya akan menjadi pizza itu sendiri:

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

    </ul>
</div>

Seperti yang Anda lihat, daftar ul dengan nama kelas yang berbicara "pizza-cards" kosong sejauh ini, kami akan memperbaiki pengawasan ini:

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

Semua kesenangan di sini ada di dalam loop. untuk setiap(var {item} dalam {items})
Ini adalah markah Razor khas yang memungkinkan kita untuk menggunakan kekuatan C # pada halaman yang sama dengan kode html biasa . Hal utama adalah menempatkan simbol "@" di depan kata kunci dan variabel bahasa .

Di dalam loop, kita cukup mengakses properti dari objek pizza .

Pada akhirnya, kami menampilkan harga dasar pizza yang diformat menggunakan metode GetFormattedBasePrice . Omong -omong, ini adalah perbedaan antara model domain BasePizza dan ViewModel -nya representasi, karena metode ini berisi logika paling sederhana untuk menampilkan harga dalam format yang diperlukan, yang tidak kita butuhkan di tingkat layanan, di mana kita entah bagaimana memanipulasi harga, tetapi tidak menunjukkannya di mana pun.

Kami menampilkan data yang diterima di browser


Kami punya semua data yang diperlukan untuk ditampilkan. Saatnya untuk meluncurkan aplikasi kami dan memastikan semuanya bekerja. Kami mengklik tombol Debug (di Rider, tombol Run hanya meluncurkan aplikasi tanpa kemampuan Debug ).

Dan ooh-ho, tidak ada yang berhasil :) Buka konsol ( F12 ) dan lihat bahwa semuanya berwarna merah, ada sesuatu yang salah. Blazor tidak begitu putus asa dalam debugging dan seluruh tumpukan panggilan dapat dilihat di Konsol , dan menurut saya ini dilakukan bahkan lebih baik daripada di Sudut yang sama . Tidak perlu menebak dengan tanda tidak langsung di mana kesalahan terjadi, lihat saja tumpukan panggilan:



Pesan NullReferenceException terjadi saat merender halaman . Bagaimana ini bisa terjadi, karena kami menginisialisasi koleksi yang kami gunakan dalam metode OnInitializedAsync .

Untuk memahami sedikit lebih baik, masukkan output waktu di tempat yang tepat untuk melihat jangka waktu dari apa yang terjadi:

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



Pada tangkapan layar di bawah, di konsol, apa yang terjadi pada saat rendering halaman. Dapat dilihat bahwa halaman mulai render bahkan sebelum penyelesaian metode asinkron.
Pada pass pertama, PizzaViewModels belum diinisialisasi dan kami menangkap NullReferenceException . Kemudian, seperti yang diharapkan setelah Tugas mengembalikan metode OnInitializedAsync ke status RanToCompletion , kontrol direkayasa ulang. Yang perlu diperhatikan, selama pass kedua, kami masuk ke siklus, seperti yang bisa dilihat dari pesan di konsol. Tetapi pada titik ini, UI tidak lagi diperbarui dan kami tidak melihat perubahan yang terlihat.



Faktanya, masalahnya sangat mudah dipecahkan, Anda hanya perlu menjalankan pemeriksaan nol secara asinkron sebelum menjalankan loop , maka pengecualian tidak akan terjadi untuk pertama kalinya dan selama pass kedua kita akan melihat data yang kita butuhkan.
@if (PizzaViewModels != null)
{
    @foreach (var pizza in PizzaViewModels)
    {
        โ€ฆโ€ฆโ€ฆโ€ฆโ€ฆโ€ฆโ€ฆโ€ฆโ€ฆ.. //    
    }
}


Tampaknya sedikit lebih baik sekarang, tidak ada lagi pesan kesalahan di konsol dan Anda dapat melihat informasi yang datang kepada kami dari server:



Ini jauh lebih baik, tetapi tidak ada cukup gaya dan sumber daya, khususnya gambar, ganti isi folder wwwroot dengan isi folder "~ / Artikel / Bagian2 /BlazingPizza.Client/wwwroot "repositori (tautan di akhir artikel) dan jalankan proyek lagi, jauh lebih baik. Meski masih jauh dari ideal:



Peristiwa Kehidupan Komponen


Karena kami sudah bertemu dengan salah satu peristiwa kehidupan komponen OnInitializedAsync, masuk akal untuk menyebutkan yang lain:
Metode inisialisasi
Diinisiasi
Disebut ketika komponen sudah diinisialisasi dan parameternya sudah ditetapkan oleh komponen induk. Selama umur komponen, ini disebut sekali setelah inisialisasi.
DiInitializedAsync
Versi asinkron dari metode pertama, setelah eksekusi, komponen di-render ulang. Oleh karena itu, ketika menulis kode, Anda perlu mempertimbangkan bahwa beberapa objek mungkin nol.
Metode yang dapat dijalankan sebelum menetapkan nilai parameter
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


Di bagian ini, kami belajar cara menerima data dari pengontrol dan menampilkannya kepada pengguna.
Di bagian selanjutnya, kita akan merapikan Tata Letak dan menambahkan lapisan akses data untuk menampilkan data nyata di halaman utama.

Tautan ke repositori dari seri artikel ini.
Tautan ke sumber asli.

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


All Articles