рдмреНрд▓реЗрдЬрд╝рд░ рдХреНрд▓рд╛рдЗрдВрдЯ рд╕рд╛рдЗрдб рдСрдирд▓рд╛рдЗрди рд╕реНрдЯреЛрд░: рднрд╛рдЧ 5 - рд░реАрд╕рд╛рдпрдХрд▓ рдмрд┐рди рджреЗрдЦреЗрдВ рдФрд░ рд╕реНрдЯреЗрдЯрдлреБрд▓ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░реЗрдВ



рдирдорд╕реНрдХрд╛рд░, рд╣реЗрдмреНрд░! рдореИрдВ рдмреНрд▓реЗрдЬрд╝рд░ рдкрд░ рдСрдирд▓рд╛рдЗрди рд╕реНрдЯреЛрд░ рдХрд░рдирд╛ рдЬрд╛рд░реА рд░рдЦрддрд╛ рд╣реВрдВред рдЗрд╕ рднрд╛рдЧ рдореЗрдВ, рдореИрдВ рдЗрд╕ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░реВрдБрдЧрд╛ рдХрд┐ рдХреИрд╕реЗ рдореИрдВрдиреЗ рдЗрд╕рдореЗрдВ рдорд╛рд▓ рдХреА рдПрдХ рдЯреЛрдХрд░реА рджреЗрдЦрдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рдЬреЛрдбрд╝реА рдФрд░ рд░рд╛рдЬреНрдп рдХреЗ рд╕рд╛рде рд╕рдВрдЧрдард┐рдд рдХрд╛рдо рдХрд┐рдпрд╛ред рд╡рд┐рд╡рд░рдг рдХреЗ рд▓рд┐рдП, рдмрд┐рд▓реНрд▓реА рдореЗрдВ рдЖрдкрдХрд╛ рд╕реНрд╡рд╛рдЧрдд рд╣реИред

рд╕рд╛рдордЧреНрд░реА



рд╕рдВрджрд░реНрдн


тЖТ  рд╕реНрд░реЛрдд
тЖТ рдбреЛрдХрд░ рд░рдЬрд┐рд╕реНрдЯреНрд░реА рдкрд░ рдЫрд╡рд┐рдпрд╛рдВ

рд╕реНрдЯреЗрдЯрдлреБрд▓


рдореБрдЭреЗ рдкрд╕рдВрдж рдирд╣реАрдВ рдерд╛ рдХрд┐ рдкреГрд╖реНрдареЛрдВ рдХреЗ рдмреАрдЪ рд╕реНрд╡рд┐рдЪ рдХрд░рддреЗ рд╕рдордп рд░рд╛рдЬреНрдп рдЦреЛ рдЬрд╛рддрд╛ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╡реЗ рдХреНрд╖реЗрддреНрд░ рдЬрд┐рдирдХреЗ рджреНрд╡рд╛рд░рд╛ рдореИрдВрдиреЗ рдЙрддреНрдкрд╛рджреЛрдВ рдХреЛ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд┐рдпрд╛ред рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдореИрдВрдиреЗ рд╕реНрдЯреЗрдЯрдлреБрд▓ рд╕рд┐рдВрдЧрд▓рдЯрди рд╕реЗрд╡рд╛рдУрдВ рдкрд░ рд╕реНрд╡рд┐рдЪ рдХрд┐рдпрд╛ рдФрд░ рдбреАрдЖрдИ рдХрдВрдЯреЗрдирд░ рдореЗрдВ рдкреЗрдЬ рдХреЗ рд╡реНрдпреВрдореЗрд▓ рдХреЛ рдПрдХ рд╕рд┐рдВрдЧрд▓рдЯрди рдХреЗ рд░реВрдк рдореЗрдВ рдкрдВрдЬреАрдХреГрдд рдХрд┐рдпрд╛ред рдЕрдирд┐рд╡рд╛рд░реНрдп рд░реВрдк рд╕реЗ, рдореИрдВрдиреЗ рдПрдХ рд╕реНрдЯреЗрдЯ рд╕реНрдЯреЛрд░ рдХреЗ рд░реВрдк рдореЗрдВ 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) рдбрд┐ рдХрдВрдЯреЗрдирд░ рдореЗрдВ рдкрдВрдЬреАрдХрд░рдг


services.AddTransient<IApiRepository, ApiRepository>();
services.AddSingleton<IBasketService, BasketService>();
services.AddSingleton<BasketViewModel>();

рдХреЛрдгреАрдп 9 рдкрд░ рд╡рд┐рдХрд▓реНрдк


рдЕрдм рддрдХ, рдмреНрд▓реЗрдЬрд╝рд░ рдкрд░ рд╡рд┐рдХрд╛рд╕ рдореБрдЭреЗ рдХреЛрдгреАрдп рд╕реЗ рдЕрдзрд┐рдХ рдЦреБрд╢реА рджреЗрддрд╛ рд╣реИред


All Articles