Как отделить код от пользовательского интерфейса в Blazor.Net - PullRequest
1 голос
/ 20 мая 2019

Ссылка на эту VisualStudioMagazine статью. Я пытаюсь разместить код в отдельном файле вместо вида бритвы.

Я пытался:

@page "/Item"
@using WebApplication1.Shared
@using WebApplication1.Client.Services;
@inject HttpClient Http
@inherits ItemComponent

@if (ItemList != null)
{
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Category</th>
                <th>Metal</th>
                <th>Price</th>
                <th>Quantity</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in ItemList)
            {
                <tr>
                    <td>@item.ID</td>
                    <td>@item.Name</td>
                    <td>@item.Category</td>
                    <td>@item.Metal</td>
                    <td>@item.Price</td>
                    <td>@item.Quantity</td>
                </tr>
            }
        </tbody>
    </table>
}

@functions{
    public ItemModel[] ItemList;
    ItemComponent IC = new ItemComponent();

    protected override async Task OnInitAsync()
    {
        ItemList = IC.GetItems().Result;
        //ItemList = await Http.GetJsonAsync<ItemModel[]>("api/Item/GetItems");
        StateHasChanged();
    }
}

И ItemComponent:

using System.Threading.Tasks;
using WebApplication1.Shared;
using System.Net.Http;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Blazor;

namespace WebApplication1.Client.Services
{
    public class ItemComponent
    {
        public async Task<ItemModel[]> GetItems()
        {
            ItemModel[] ItemList;
            HttpClient Http = new HttpClient();
            ItemList = await Http.GetJsonAsync<ItemModel[]>("api/Item/GetItems");
            return ItemList;
        }

    }
}

Но это не работает, это показывает, что:

Код серьезности Описание Состояние подавления строки файла проекта Ошибка CS0115 «Item.BuildRenderTree (RenderTreeBuilder)»: не найден подходящий метод для переопределения WebApplication1.Client D: \ Other \ blazor \ WebApplication1.Client \ obj \ Debug \ netstandard2.0 \ RazorDeclaration \ Pages \ ItemModule \ Item.razor.g .cs 30 Активный

Также согласно странице учебника нельзя наследовать от BlazorComponent до ItemComponent, поскольку не удалось получить ссылку.

Есть ли способ отделить большую часть кода от представления Blazor в отдельный файл кода?

Обновление 1

После внесения изменений в соответствии с Крис-ответом выдается исключение

System.Net.Http.HttpRequestException: Невозможно установить соединение, так как целевой компьютер активно отказал ему. ---> System.Net.Sockets.SocketException: не может быть установлено соединение потому что целевая машина активно отказывалась. в System.Net.Http.ConnectHelper.ConnectAsync (Строковый хост, порт Int32, CancellationToken cancellationToken) --- Конец внутреннего исключения трассировка стека --- в System.Net.Http.ConnectHelper.ConnectAsync (Строковый хост, порт Int32, Отмена взята Отмена взята) в System.Threading.Tasks.ValueTask 1.get_Result() at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken)<br> at System.Threading.Tasks.ValueTask 1.get_Result () в System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync (HttpRequestMessage запрос, CancellationToken CancellationToken) в System.Threading.Tasks.ValueTask 1.get_Result() at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Threading.Tasks.ValueTask 1.get_Result () в System.Net.Http.HttpConnectionPool.SendWithRetryAsync (HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
в System.Net.Http.RedirectHandler.SendAsync (HttpRequestMessage запрос, CancellationToken CancellationToken) в System.Net.Http.HttpClient.FinishSendAsyncUnbuffered (Task 1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts) at System.Net.Http.HttpClient.GetStringAsyncCore(Task 1 getTask) в Microsoft.AspNetCore.Builder.BlazorMonoDebugProxyAppBuilderExtensions.GetOpenedBrowserTabs (String debuggerHost) в Microsoft.AspNetCore.Builder.BlazorMonoDebugProxyAppBuilderExtensions.DebugHome (HttpContext контекст)

Ответы [ 3 ]

5 голосов
/ 20 мая 2019

Вам просто нужно наследовать от ComponentBase в вашем ItemComponent классе, как этот.

public class ItemComponent : ComponentBase
{
    public async Task<ItemModel[]> GetItems()
    {
        ItemModel[] ItemList;
        HttpClient Http = new HttpClient();
        ItemList = await Http.GetJsonAsync<ItemModel[]>("api/Item/GetItems");
        return ItemList;
    }
}

Эта статья несколько устарела, поскольку BlazorComponent была недавно переименована.

Просто убедитесь, что весь код, который имеется в блоке functions вашего представления, перемещен в базовый класс, поскольку смешение двух подходов может иметь странные побочные эффекты.

3 голосов
/ 20 мая 2019

У вас есть два варианта. Первое уже упоминалось Крисом Сэйнти. Создайте класс, который наследуется от ComponentBase, и наследуйте его в представлении Razor.

Ваш класс будет определен как: public class MyBaseClass : ComponentBase

И в вашем представлении Razor вы используете: @inherits MyBaseClass

Благодаря этому MyBaseClass становится страницей кода для вашего представления Razor и может переопределять все события жизненного цикла для представления.

Второй вариант - создать ViewModel. Вы создаете стандартный класс C # и внедряете его в представление Razor, используя внедрение свойства.

Вы обычно определяете свой класс: public class MyViewModel

И добавьте его в свой вид Razor: @inject MyViewModel

Этот класс ViewModel не знает о событиях жизненного цикла страницы и не зависит ни от чего, связанного с Blazor. Если вы просто хотите связать свое представление Razor с объектом и вам нужно что-то, что можно использовать повторно (или вы хотите поместить его в общий проект), это может быть хорошим выбором.

Вы можете использовать унаследованный код позади и внедренную ViewModel в том же Razor View, если у вас есть необходимость или если вы хотите сохранить код жизненного цикла страницы отдельно от привязок данных.

2 голосов
/ 20 мая 2019

Здесь также есть другое решение, подобное точке Луи Хендрика , которое:

Вы можете использовать наследуемый код и внедренную ViewModel на тот же Razor View, если у вас есть необходимость или если вы хотите сохранить страницу код жизненного цикла отдельно от привязок данных.

Рассматривать 'состояние' как альтернативу моделям вида

В мире React / Redux / Flux часто обсуждается понятие «Состояние», которое относится к объекту (или объектам), который содержит весь статус приложения или областей приложения) в определенный момент времени. , В представлении Flux о мире каждое действие в приложении переводит приложение из его существующего состояния в новое состояние.

Состояние приложения состоит из текущих значений данных (например, скажем, contact.firstname), а также состояния пользовательского интерфейса (например, включена ли эта кнопка).

Это достигается в основном тем же способом, что и подход ViewModel, но подход State часто группирует код по поведению (например, все состояния, связанные с заказом Pizza, так, из чего состоит текущая Pizza, а также какие элементы пользовательского интерфейса должны отображаться, если заказ обрабатывается) и признает, что состояние может отображаться несколькими компонентами, поэтому объекты состояния не обязательно будут отображаться напрямую в один файл бритвы, как это обычно делает ViewModel.

Отличный пример и учебное пособие, предоставлено командой .NET

Это проще с примером, и, к счастью, мастерская блейзора Blazing Pizza команды Microsoft Blazor обеспечивает превосходный результат.

В качестве быстрого примера из этого урока - это OrderState класс , в котором хранится текущее состояние, относящееся к текущему порядку:

    public class OrderState
    {
        public event EventHandler StateChanged;

        public bool ShowingConfigureDialog { get; private set; }

        public Pizza ConfiguringPizza { get; private set; }

        public Order Order { get; private set; } = new Order();

        public void ShowConfigurePizzaDialog(PizzaSpecial special)
        {
            ConfiguringPizza = new Pizza()
            {
                Special = special,
                SpecialId = special.Id,
                Size = Pizza.DefaultSize,
                Toppings = new List<PizzaTopping>(),
            };

            ShowingConfigureDialog = true;
        }

        public void CancelConfigurePizzaDialog()
        {
            ConfiguringPizza = null;

            ShowingConfigureDialog = false;
            StateHasChanged();
        }

        public void ConfirmConfigurePizzaDialog()
        {
            Order.Pizzas.Add(ConfiguringPizza);
            ConfiguringPizza = null;

            ShowingConfigureDialog = false;
            StateHasChanged();
        }

        public void RemoveConfiguredPizza(Pizza pizza)
        {
            Order.Pizzas.Remove(pizza);
            StateHasChanged();
        }

        public void ResetOrder()
        {
            Order = new Order();
        }

        private void StateHasChanged()
        {
            StateChanged?.Invoke(this, EventArgs.Empty);
        }
    } ```

Обратите внимание, что в этом классе состояний нет концепции связанного с ним пользовательского интерфейса, но у него есть свойства, которые управляют поведением пользовательского интерфейса.

В этом примере классы-бритвы все еще имеют блоки @functions, но они значительно упрощаются благодаря введению свойств в классе State, которые играют явную роль в управлении поведением пользовательского интерфейса (например, ShowingConfigureDialog). Например, из index.razor :

    <ul class="pizza-cards">
        @if (specials != null)
        {
            @foreach (var special in specials)
            {
                <li onclick="@(() => OrderState.ShowConfigurePizzaDialog(special))"
style="background-image: url('@special.ImageUrl')">
                    <div class="pizza-info">
                        <span class="title">@special.Name</span>
                        @special.Description
                        <span class="price">@special.GetFormattedBasePrice()</span>
                    </div>
                </li>
            }
        }
    </ul> </div> ```

Весь этот учебник превосходен, я настоятельно рекомендую поработать над ним.

Но я не хочу код C # в моих файлах бритвы ...

Вы все еще можете поместить код из блока @functions в файл базового класса, а также использовать подход состояния.

Причина, по которой люди склонны этого не делать, заключается в том, что если ваш файл состояния управляет поведением пользовательского интерфейса, тогда код проводки @functions обычно заканчивается в виде нескольких строк, поэтому часто кажется, что его не стоит помещать в отдельный файл.

...