#c# #asp.net-core #generics #blazor
#c# #asp.net-core #дженерики #blazor
Вопрос:
У меня есть простой компонент Blazor, который выполняет итерацию по общему списку :
@typeparam TItem;
@foreach (var item in List)
{
@ElementTemplate(item); // What code should be here to pass i to the ElementTemplate????
i ;
}
@code {
int i = 0;
[Parameter] public List<TItem> List { get; set; }
[Parameter] public RenderFragment<TItem> ElementTemplate { get; set; }
}
У меня есть еще один простой компонент, который получит элемент и индекс для отображения данных (сотрудник):
<div> @Index . @Person.Name </div>
@code{
[Parameter] public Person { get; set; }
[Parameter] public int Index { get; set; }
}
И на моей главной странице у меня есть следующее:
<GenericList List="employees">
<ElementTemplate>
<Employee Person="context" Index="?"></Employee>
</ElementTemplate>
</GenericList>
Как вы можете видеть, что компоненту Employee требуется параметр Index, как я могу передать индекс из компонента GenericList?
В этом примере переменная ‘i’ должна быть передана в ElementTemplate, а также сам общий объект TItem.
Комментарии:
1. Вы не можете — вы явно указали, что
ElementTemplate
принимаете один элемент данных. Если вы хотите, чтобы он содержал несколько элементов, вы могли бы наследовать его от базового класса или реализовать интерфейс и использовать его в качестве общего ограничения. Или создайте элемент данныхdynamic
, потеряв преимущества дженериков2. Вы можете настроить словарь для сопоставления вашего типа модели с компонентом для рендеринга. Используйте это для передачи списка. Я расширил
ComponentBase
для сопоставленных компонентов,ViewComponentBase<TModel> : ComponentBase
Ответ №1:
Я считаю, что быстрый / простой подход заключается в использовании кортежа в качестве контекста RenderFragment:
@foreach (var item in List)
{
@ElementTemplate((item,i));
i ;
}
@code {
int i=0;
[Parameter] public List<TItem> List { get; set; }
[Parameter] public RenderFragment<(TItem item, int index)> ElementTemplate { get; set; }
}
Тогда ваша разметка становится:
<GenericList List="employees">
<ElementTemplate>
<Employee @key=@context.index Person=@context.item Index=@context.index></Employee>
</ElementTemplate>
</GenericList>
Если вы предпочитаете, вы можете использовать служебный класс в GenericList вместо кортежа.
Комментарии:
1. Мне нравится кортеж, но не
@key=context
будет или, по крайней мере@key=context.item
, будет лучше? индекс наименее связан с контентом сотрудника. Предположим, вы удалили элемент.2. Ну, я не хотел вдаваться во всю проблему того, как эти целые числа индексов на самом деле ненадежно связаны с данными, и просто хотел вставить это
@key
туда — используя самый быстрый доступный тип. Но вы можете быть правы — все зависит от фактического реального контента.
Ответ №2:
Вы можете определить сложный класс для вашего контекста
В GenericList.razon определите класс:
public class ListContext
{
public TItem Item {get; set;}
public int Index {get; set;}
}
и используйте этот класс в качестве типа контекста для вашего шаблона:
[Parameter]
public RenderFragment<ListContext> ElementTemplate { get; set; }
затем, когда вы вызываете этот шаблон, вы можете просто создать новый класс и задать правильные параметры:
@foreach (var item in List)
{
@ElementTemplate(new ListContext{Item = item, Index = i});
i ;
}
вот рабочий пример этого: https://blazorrepl.com/repl/cuvakzPF21sXSZGK54
Комментарии:
1. @Спасибо, но это не тот способ, которым я хотел это сделать.
2. Чего именно вы хотите достичь? Может быть, передача индекса из компонента GenericList с использованием каскадного значения будет работать?