Обязательный ребенок с дополнительной выборкой данных
Я пытаюсь выяснить вид основного / подробного представления некоторых простых данных. У меня есть список рецептов, и у каждого рецепта есть список ингредиентов.
У меня есть страница рецептов, которая перечисляет все рецепты, и это работает хорошо. У меня есть бритвенный компонент 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;
}