Обязательный ребенок с дополнительной выборкой данных

Я пытаюсь выяснить вид основного / подробного представления некоторых простых данных. У меня есть список рецептов, и у каждого рецепта есть список ингредиентов.

У меня есть страница рецептов, которая перечисляет все рецепты, и это работает хорошо. У меня есть бритвенный компонент RecipeDetail, который должен извлекать список ингредиентов после выбора рецепта на странице рецептов.

Я перепробовал разные вещи. Я попытался связать данные, передав рецепт и его компоненты полностью заполненными компоненту RecipeDetail, но из-за структуры данных метод GetJsonAsync обнаруживает циклическую ссылку и выдает исключение. Я попытался передать идентификатор выбранного рецепта со страницы рецептов на страницу RecipeDetail через параметр, связанный с переменной на странице рецептов, но выборка данных никогда не запускается.

@page "/recipes"
@using BlazorApp.Shared.Models
@inject HttpClient Http

<h1>Recipes</h1>

@if(RecipesArray != null)
{
    @foreach (var r in RecipesArray)
    {
        <h3 @onclick="@(() => SelectRecipe(r))">@r.Title</h3>
    }
}

<RecipeDetail RecipeId="@SelectedRecipeId"></RecipeDetail>

@code {
    Recipe[] RecipesArray;
    int SelectedRecipeId;

    protected override async Task OnInitializedAsync()
    {
        RecipesArray = await Http.GetJsonAsync<Recipe[]>("Recipes");
    }

    protected void SelectRecipe(Recipe recipe)
    {
        SelectedRecipeId = recipe.Id;
    }
}

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

@using BlazorApp.Shared.Models
@inject HttpClient Http

<h1>RecipeId = @selectedRecipeId</h1>
@if (Ingredients != null && Ingredients.Length > 0)
{
    <h1>@RecipeId</h1>
    <ul>
        @foreach (var item in Ingredients)
        {
            <li>@item.Ingredient.ToString()</li>
        }
    </ul>
}


@code {
    private int selectedRecipeId;
    RecipeIngredient[] Ingredients;

    [Parameter]
    public int RecipeId
    {
        get { return selectedRecipeId; }
        set
        {
            selectedRecipeId = value;
            //if (value > 0)
            //    Ingredients = Http.GetJsonAsync<RecipeIngredient[]>($"Recipes/{RecipeId}").Result;
        }
    }
}

Я был бы рад исправить ошибку JSON и просто привязать к переданному объекту, но я также не смог найти решение этой проблемы. Я попробовал атрибут [JsonIgnore], но это не помогло.

Даже если выборка Json была исправлена, я думаю, что у меня все еще есть вариант использования для повторной выборки данных после изменения параметра, поэтому я хотел бы понять, что я делаю неправильно в обоих случаях.

3 ответа

Решение

Это решение, которое я придумал для RecipeDetail

@using BlazorApp.Shared.Models
@inject HttpClient Http

@if (Ingredients != null && Ingredients.Length > 0)
{
    <ul>
        @foreach (var item in Ingredients)
        {
            <li>@item.ToString()</li>
        }
    </ul>
}


@code {
    RecipeIngredient[] Ingredients;

    [Parameter]
    public int RecipeId { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        if (RecipeId != 0)
        {
            await LoadRecipeDetails(RecipeId);
        }
    }

    private async Task LoadRecipeDetails(int RecipeId)
    {
        Ingredients = await Http.GetJsonAsync<RecipeIngredient[]>($"Recipes/{RecipeId}");
    }
}

Чтобы передать параметры компонента, вы должны определить два свойства параметра в вашем дочернем компоненте:

@code {
    [Parameter]
    public int RecipeId { get; set; }

    [Parameter]
    public EventCallback<int> RecipeIdChanged { get; set; }
}

Теперь, когда вы выбираете рецепт, в родительском компоненте, таким образом, изменяя значение переменной SelectedRecipeId, значение свойства параметра RecipeId обновляется, так что вы можете использовать новое значение любым удобным вам способом, включая отправку http-запросов в сервер.

В родительском компоненте у вас должно быть это

<RecipeDetail @bind-RecipeId="@SelectedRecipeId"></RecipeDetail>

Запись <RecipeDetail @bind-RecipeId="@SelectedRecipeId"></RecipeDetail> по сути эквивалентно написанию:

<RecipeDetail @bind-RecipeId="SelectedRecipeId" @bind-RecipeId:event="RecipeIdChanged" />

Примечание. Другой способ передачи значений от родительского компонента к дочерним компонентам заключается в использовании шаблона AppState. Вы можете увидеть, как этот шаблон используется здесь

Вы также можете использовать компонент CascadingValue, чтобы получить ту же функциональность

Надеюсь это поможет

Попробуйте (я не проверял это)...

@page "/recipes"
@using BlazorApp.Shared.Models
@inject HttpClient Http

<h1>Recipes</h1>

@if(RecipesArray != null)
{
    @foreach (var r in RecipesArray)
    {
        <h3 @onclick="@(() => SelectRecipe(r))">@r.Title</h3>
    }
}

<RecipeDetail RecipeId="@SelectedRecipeId" Ingredients="@Ingredients"></RecipeDetail>

@code {
    Recipe[] RecipesArray;
    int SelectedRecipeId;
    RecipeIngredient[] Ingredients = new RecipeIngredient[]();

    protected override async Task OnInitializedAsync()
    {
        RecipesArray = await Http.GetJsonAsync<Recipe[]>("Recipes");
    }

    protected void SelectRecipe(Recipe recipe)
    {
        SelectedRecipeId = recipe.Id;
        if (value > 0)
        {
          Ingredients = Http.GetJsonAsync<RecipeIngredient[]>($"Recipes/{SelectedRecipeId}").Result;
        }
    }
}

and:

<h1>RecipeId = @selectedRecipeId</h1>
@if (Ingredients != null && Ingredients.Length > 0)
{
    <h1>@RecipeId</h1>
    <ul>
        @foreach (var item in Ingredients)
        {
            <li>@item.Ingredient.ToString()</li>
        }
    </ul>
}


@code {
    [Parameter]
    public int RecipeId;

    [Parameter]
    public RecipeIngredient[] Ingredients;
}
Другие вопросы по тегам