Передача дополнительных данных в общий фрагмент рендеринга в компоненте Blazor

#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 с использованием каскадного значения будет работать?