哈Ha!我继续在Blazor上做在线商店。在本部分中,我将讨论如何增加查看一篮子商品的功能以及如何与州组织工作。有关详细信息,欢迎来猫。内容
参考文献
→ 来源→ Docker注册表上的映像有状态的
我不喜欢在页面之间切换时丢失状态。例如,我过滤产品的那些字段。为了解决此问题,我切换到有状态的单例服务,并在DI容器中将页面的ViewModel注册为单例。本质上,我使用DI容器作为状态存储,并且ViewModel开始作为服务注入到View中。编码
1)型号
public sealed class ProductModel
{
public Guid Id { get; set; }
public string Version { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
}
public class BasketLineModel
{
public uint Quantity { get; set; }
public ProductModel Product { get; set; }
}
public class BasketModel
{
public List<BasketLineModel> Lines { get; set; } = new List<BasketLineModel>();
}
2)服务
public class BasketService : IBasketService
{
private readonly IApiRepository _repository;
private BasketModel _basket;
public BasketService(IApiRepository repository)
{
_repository = repository;
_basket = new BasketModel();
}
public string Error { get; private set; }
public IReadOnlyList<BasketLineModel> Model => _basket.Lines.AsReadOnly();
public event EventHandler OnBasketItemsCountChanged;
public long ItemsCount => _basket?.Lines?.Sum(l => l.Quantity) ?? 0;
public async Task Load()
{
var count = ItemsCount;
var (r, e) = await _repository.GetBasket();
_basket = r;
Error = e;
if (string.IsNullOrWhiteSpace(Error) && count != ItemsCount)
OnBasketItemsCountChanged?.Invoke(null, null);
}
public async Task Add(ProductModel product)
{
var (_, e) = await _repository.AddToBasket(product);
Error = e;
if (!string.IsNullOrWhiteSpace(e))
return;
await Load();
}
public async Task Remove(ProductModel product)
{
var (_, e) = await _repository.Remove(product.Id);
Error = e;
if (!string.IsNullOrWhiteSpace(e))
return;
await Load();
}
}
这里有必要讲一下public event EventHandler OnBasketItemsCountChanged;
我想在页面标题的购物篮中显示当前产品数量。问题在于标题不是购物车页面的子项,因此,标题将忽略更新其状态。这样他就重画了I并添加了此事件,并在其中悬挂了这样一个处理程序:@using BlazorEShop.Spa.BlazorWasm.Client.Core.Services
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject NavigationManager Navigation
@inject SignOutSessionStateManager SignOutManager
@inject IBasketService Basket
@implements IDisposable
<AuthorizeView>
<Authorized>
<span class="text-success">Total Items In Basket: @TotalItemsCount </span>
Hello, @context?.User?.Identity?.Name!
<button class="nav-link btn btn-link" @onclick="BeginSignOut">Log out</button>
</Authorized>
<NotAuthorized>
<a href="authentication/login">Log in</a>
</NotAuthorized>
</AuthorizeView>
@code
{
public long TotalItemsCount { get; set; }
protected override void OnInitialized()
{
Basket.OnBasketItemsCountChanged += Bind;
TotalItemsCount = Basket.ItemsCount;
base.OnInitialized();
}
public void Dispose()
{
Basket.OnBasketItemsCountChanged -= Bind;
}
public void Bind(object s, EventArgs e)
{
if (TotalItemsCount == Basket.ItemsCount)
return;
TotalItemsCount = Basket.ItemsCount;
this.StateHasChanged();
}
private async Task BeginSignOut(MouseEventArgs args)
{
await SignOutManager.SetSignOutState();
Navigation.NavigateTo("authentication/logout");
}
}
3)ViewModel
public class BasketViewModel
{
private bool _isInitialized;
private readonly IBasketService _service;
public BasketViewModel(IBasketService service)
{
_service = service;
}
public string Error => _service.Error;
public IReadOnlyList<BasketLineModel> Model => _service.Model;
public async Task OnInitializedAsync()
{
if (_isInitialized)
return;
Console.WriteLine("BASKET INIT!");
await _service.Load();
_isInitialized = true;
}
public Task Add(ProductModel product) => _service.Add(product);
public Task Remove(ProductModel product) => _service.Remove(product);
}
4)查看
@page "/basket"
@attribute [Authorize]
@inject BasketViewModel ViewModel
<h3>Basket</h3>
<Error Model="@ViewModel.Error" />
<input type="button" class="btn btn-primary my-3" value="Create Order" /> <!--TODO: -->
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Title</th>
<th>Price</th>
<th>Quantity</th>
<th></th>
</tr>
</thead>
<tbody>
@if (ViewModel.Model == null)
{
<tr>
<td>
<em>Loading...</em>
</td>
</tr>
}
else
{
foreach (var line in ViewModel.Model)
{
<tr>
<td>@line.Product.Title</td>
<td>@line.Product.Price</td>
<td>@line.Quantity</td>
<td>
<input type="button"
class="btn btn-success"
@onclick="@(async x=>await ViewModel.Add(line.Product))"
value="+" />
<input class="btn btn-warning"
value="-"
type="button"
@onclick="@(async x=>await ViewModel.Remove(line.Product))" />
</td>
</tr>
}
}
</tbody>
</table>
</div>
@code
{
protected override async Task OnInitializedAsync()
{
await ViewModel.OnInitializedAsync();
}
}
5)在DI容器中注册
services.AddTransient<IApiRepository, ApiRepository>();
services.AddSingleton<IBasketService, BasketService>();
services.AddSingleton<BasketViewModel>();
Angular 9上的选项
到目前为止,Blazor的开发给我带来了比Angular更多的乐趣。