Шаблонные компоненты Blazor в ASP.NET Core
Примечание.
Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 9 этой статьи.
Предупреждение
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 9 этой статьи.
Внимание
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
В текущем выпуске см . версию .NET 9 этой статьи.
Роберт Хакен
В этой статье приведены сведения о шаблонных компонентах, которые могут принимать один или несколько шаблонов пользовательского интерфейса в качестве параметров, используемых затем как часть логики отрисовки компонента.
Шаблонные компоненты
Шаблонные компоненты — это компоненты, которые получают один или несколько шаблонов пользовательского интерфейса в качестве параметров, которые можно использовать в логике отрисовки компонента. С помощью шаблонных компонентов можно создавать компоненты более высокого уровня, которые являются более повторно используемыми. Вот некоторые примеры.
- компонент таблицы, позволяющий пользователю задавать шаблоны для заголовка, строк и нижнего колонтитула таблицы;
- компонент списка, позволяющий пользователю задавать шаблон для отрисовываемых элементов списка.
- Компонент панели навигации, позволяющий пользователю указать шаблон для запуска содержимого и ссылок навигации.
Шаблонный компонент определяется путем указания одного или нескольких параметров компонента типа RenderFragment или RenderFragment<TValue>. Фрагмент отрисовки представляет часть пользовательского интерфейса, подлежащую отрисовке. RenderFragment<TValue> принимает параметр типа, который можно задать при вызове фрагмента отрисовки.
Примечание.
Дополнительные сведения о RenderFragment см. в статье Компоненты Razor ASP.NET Core.
Часто шаблонные компоненты обычно типитируются, как показано в следующем TemplatedNavBar
компоненте(TemplatedNavBar.razor
). Универсальный тип (<T>
) в следующем примере используется для отображения IReadOnlyList<T> значений, которые в данном случае являются списком домашних животных для компонента, отображающего панель навигации со ссылками на компонент сведений о домашних животных.
TemplatedNavBar.razor
:
@typeparam TItem
<nav class="navbar navbar-expand navbar-light bg-light">
<div class="container justify-content-start">
@StartContent
<div class="navbar-nav">
@foreach (var item in Items)
{
@ItemTemplate(item)
}
</div>
</div>
</nav>
@code {
[Parameter]
public RenderFragment? StartContent { get; set; }
[Parameter, EditorRequired]
public RenderFragment<TItem> ItemTemplate { get; set; } = default!;
[Parameter, EditorRequired]
public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem
<nav class="navbar navbar-expand navbar-light bg-light">
<div class="container justify-content-start">
@StartContent
<div class="navbar-nav">
@foreach (var item in Items)
{
@ItemTemplate(item)
}
</div>
</div>
</nav>
@code {
[Parameter]
public RenderFragment? StartContent { get; set; }
[Parameter, EditorRequired]
public RenderFragment<TItem> ItemTemplate { get; set; } = default!;
[Parameter, EditorRequired]
public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem
<nav class="navbar navbar-expand navbar-light bg-light">
<div class="container justify-content-start">
@StartContent
<div class="navbar-nav">
@foreach (var item in Items)
{
@ItemTemplate(item)
}
</div>
</div>
</nav>
@code {
[Parameter]
public RenderFragment? StartContent { get; set; }
[Parameter, EditorRequired]
public RenderFragment<TItem> ItemTemplate { get; set; } = default!;
[Parameter, EditorRequired]
public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem
<nav class="navbar navbar-expand navbar-light bg-light">
<div class="container justify-content-start">
@StartContent
<div class="navbar-nav">
@foreach (var item in Items)
{
@ItemTemplate(item)
}
</div>
</div>
</nav>
@code {
[Parameter]
public RenderFragment? StartContent { get; set; }
[Parameter, EditorRequired]
public RenderFragment<TItem> ItemTemplate { get; set; } = default!;
[Parameter, EditorRequired]
public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem
<nav class="navbar navbar-expand navbar-light bg-light">
<div class="container justify-content-start">
@StartContent
<div class="navbar-nav">
@foreach (var item in Items)
{
@ItemTemplate(item)
}
</div>
</div>
</nav>
@code {
[Parameter]
public RenderFragment StartContent { get; set; }
[Parameter]
public RenderFragment<TItem> ItemTemplate { get; set; } = default;
[Parameter]
public IReadOnlyList<TItem> Items { get; set; } = default;
}
@typeparam TItem
<nav class="navbar navbar-expand navbar-light bg-light">
<div class="container justify-content-start">
@StartContent
<div class="navbar-nav">
@foreach (var item in Items)
{
@ItemTemplate(item)
}
</div>
</div>
</nav>
@code {
[Parameter]
public RenderFragment StartContent { get; set; }
[Parameter]
public RenderFragment<TItem> ItemTemplate { get; set; } = default;
[Parameter]
public IReadOnlyList<TItem> Items { get; set; } = default;
}
При использовании шаблонного компонента параметры шаблона можно задать с помощью дочерних элементов, имена которых совпадают с именами параметров. В следующем примере элементы <StartContent>...</StartContent>
и <ItemTemplate>...</ItemTemplate>
указывают шаблоны RenderFragment<TValue> для элементов StartContent
и ItemTemplate
компонента TemplatedNavBar
.
Укажите атрибут Context
в элементе компонента, если необходимо указать имя параметра содержимого для неявного дочернего содержимого (без содержащего дочернего элемента). В следующем примере элемент TemplatedNavBar
имеет атрибут Context
, который применяется ко всем параметрам шаблона RenderFragment<TValue>.
Pets1.razor
:
@page "/pets-1"
<PageTitle>Pets 1</PageTitle>
<h1>Pets Example 1</h1>
<TemplatedNavBar Items="pets" Context="pet">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-1")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-1"
<PageTitle>Pets 1</PageTitle>
<h1>Pets Example 1</h1>
<TemplatedNavBar Items="pets" Context="pet">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-1")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-1"
<PageTitle>Pets 1</PageTitle>
<h1>Pets Example 1</h1>
<TemplatedNavBar Items="pets" Context="pet">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-1")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-1"
<PageTitle>Pets 1</PageTitle>
<h1>Pets Example 1</h1>
<TemplatedNavBar Items="pets" Context="pet">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-1"
<h1>Pets Example 1</h1>
<TemplatedNavBar Items="pets" Context="pet">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string Name { get; set; }
}
}
@page "/pets-1"
<h1>Pets Example 1</h1>
<TemplatedNavBar Items="pets" Context="pet">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string Name { get; set; }
}
}
Кроме того, вы можете изменить имя параметра с помощью атрибута Context
в дочернем компоненте RenderFragment<TValue>. В следующем примере Context
задано ItemTemplate
значение, а не TemplatedNavBar
.
Pets2.razor
:
@page "/pets-2"
<PageTitle>Pets 2</PageTitle>
<h1>Pets Example 2</h1>
<TemplatedNavBar Items="pets">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate Context="pet">
<NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-2")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-2"
<PageTitle>Pets 2</PageTitle>
<h1>Pets Example 2</h1>
<TemplatedNavBar Items="pets">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate Context="pet">
<NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-2")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-2"
<PageTitle>Pets 2</PageTitle>
<h1>Pets Example 2</h1>
<TemplatedNavBar Items="pets">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate Context="pet">
<NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-2")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-2"
<PageTitle>Pets 2</PageTitle>
<h1>Pets Example 2</h1>
<TemplatedNavBar Items="pets">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate Context="pet">
<NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-2"
<h1>Pets Example 2</h1>
<TemplatedNavBar Items="pets">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate Context="pet">
<NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string Name { get; set; }
}
}
@page "/pets-2"
<h1>Pets Example 2</h1>
<TemplatedNavBar Items="pets">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate Context="pet">
<NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string Name { get; set; }
}
}
Аргументы компонента с типом RenderFragment<TValue> имеют неявный параметр context
, который можно использовать. В приведенном ниже примере атрибут Context
не задан. @context.{PROPERTY}
предоставляет значения домашних животных для шаблона, где {PROPERTY}
— это свойство Pet
:
Pets3.razor
:
@page "/pets-3"
<PageTitle>Pets 3</PageTitle>
<h1>Pets Example 3</h1>
<TemplatedNavBar Items="pets">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{context.PetId}?ReturnUrl=%2Fpets-3")" class="nav-link">
@context.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-3"
<PageTitle>Pets 3</PageTitle>
<h1>Pets Example 3</h1>
<TemplatedNavBar Items="pets">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{context.PetId}?ReturnUrl=%2Fpets-3")" class="nav-link">
@context.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-3"
<PageTitle>Pets 3</PageTitle>
<h1>Pets Example 3</h1>
<TemplatedNavBar Items="pets">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{context.PetId}?ReturnUrl=%2Fpets-3")" class="nav-link">
@context.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-3"
<PageTitle>Pets 3</PageTitle>
<h1>Pets Example 3</h1>
<TemplatedNavBar Items="pets">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{context.PetId}")" class="nav-link">
@context.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-3"
<h1>Pets Example 3</h1>
<TemplatedNavBar Items="pets">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{context.PetId}")" class="nav-link">
@context.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string Name { get; set; }
}
}
@page "/pets-3"
<h1>Pets Example 3</h1>
<TemplatedNavBar Items="pets">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{context.PetId}")" class="nav-link">
@context.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string Name { get; set; }
}
}
При использовании компонентов универсального типа параметр типа выводится, если это возможно. Однако вы можете явно указать тип с помощью атрибута, имя которого соответствует имени параметра типа (TItem
в предыдущем примере):
Pets4.razor
:
@page "/pets-4"
<PageTitle>Pets 4</PageTitle>
<h1>Pets Example 4</h1>
<TemplatedNavBar Items="pets" Context="pet" TItem="Pet">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-4")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
new Pet { PetId = 4, Name = "Salem Saberhagen" },
new Pet { PetId = 7, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-4"
<PageTitle>Pets 4</PageTitle>
<h1>Pets Example 4</h1>
<TemplatedNavBar Items="pets" Context="pet" TItem="Pet">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-4")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
new Pet { PetId = 4, Name = "Salem Saberhagen" },
new Pet { PetId = 7, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-4"
<PageTitle>Pets 4</PageTitle>
<h1>Pets Example 4</h1>
<TemplatedNavBar Items="pets" Context="pet" TItem="Pet">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-4")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
new Pet { PetId = 4, Name = "Salem Saberhagen" },
new Pet { PetId = 7, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-4"
<PageTitle>Pets 4</PageTitle>
<h1>Pets Example 4</h1>
<TemplatedNavBar Items="pets" Context="pet" TItem="Pet">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
new Pet { PetId = 4, Name = "Salem Saberhagen" },
new Pet { PetId = 7, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-4"
<h1>Pets Example 4</h1>
<TemplatedNavBar Items="pets" Context="pet" TItem="Pet">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
new Pet { PetId = 4, Name = "Salem Saberhagen" },
new Pet { PetId = 7, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string Name { get; set; }
}
}
@page "/pets-4"
<h1>Pets Example 4</h1>
<TemplatedNavBar Items="pets" Context="pet" TItem="Pet">
<StartContent>
<a href="/" class="navbar-brand">PetsApp</a>
</StartContent>
<ItemTemplate>
<NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
@pet.Name
</NavLink>
</ItemTemplate>
</TemplatedNavBar>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
new Pet { PetId = 4, Name = "Salem Saberhagen" },
new Pet { PetId = 7, Name = "K-9" }
};
private class Pet
{
public int PetId { get; set; }
public string Name { get; set; }
}
}
В примере, приведенном TemplatedNavBar
в компоненте (TemplatedNavBar.razor
), предполагается, что Items
коллекция не изменяется после первоначальной отрисовки или что, если она изменится, сохранение состояния компонентов или элементов, используемых в ItemTemplate
ней, не требуется. Сведения о шаблонных компонентах, в которых такое использование не может быть предвидено, см . в разделе "Сохранение отношений с @key
разделом".
Сохранение связей с помощью @key
Шаблонные компоненты часто используются для отображения коллекций элементов, таких как таблицы или списки. В таких общих сценариях нельзя предположить, что пользователь будет избегать компонентов и элементов с отслеживанием состояния в определении шаблона элемента или что в коллекции не будут вноситься дополнительные изменения Items
. Для таких шаблонных компонентов необходимо сохранить связи с атрибутом @key
директивы.
Примечание.
Дополнительные сведения об атрибуте @key
директивы см. в разделе "Сохранение элементов, компонентов и моделей" в ASP.NET Core Blazor.
TableTemplate
Следующий компонент (TableTemplate.razor
) демонстрирует шаблонный компонент, который сохраняет связи с @key
.
TableTemplate.razor
:
@typeparam TItem
<table class="table">
<thead>
<tr>@TableHeader</tr>
</thead>
<tbody>
@foreach (var item in Items)
{
<tr @key="@item">@RowTemplate(item)</tr>
}
</tbody>
</table>
@code {
[Parameter]
public RenderFragment? TableHeader { get; set; }
[Parameter, EditorRequired]
public RenderFragment<TItem> RowTemplate { get; set; } = default!;
[Parameter, EditorRequired]
public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem
<table class="table">
<thead>
<tr>@TableHeader</tr>
</thead>
<tbody>
@foreach (var item in Items)
{
<tr @key="@item">@RowTemplate(item)</tr>
}
</tbody>
</table>
@code {
[Parameter]
public RenderFragment? TableHeader { get; set; }
[Parameter, EditorRequired]
public RenderFragment<TItem> RowTemplate { get; set; } = default!;
[Parameter, EditorRequired]
public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem
<table class="table">
<thead>
<tr>@TableHeader</tr>
</thead>
<tbody>
@foreach (var item in Items)
{
<tr @key="@item">@RowTemplate(item)</tr>
}
</tbody>
</table>
@code {
[Parameter]
public RenderFragment? TableHeader { get; set; }
[Parameter, EditorRequired]
public RenderFragment<TItem> RowTemplate { get; set; } = default!;
[Parameter, EditorRequired]
public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem
<table class="table">
<thead>
<tr>@TableHeader</tr>
</thead>
<tbody>
@foreach (var item in Items)
{
<tr @key="@item">@RowTemplate(item)</tr>
}
</tbody>
</table>
@code {
[Parameter]
public RenderFragment? TableHeader { get; set; }
[Parameter, EditorRequired]
public RenderFragment<TItem> RowTemplate { get; set; } = default!;
[Parameter, EditorRequired]
public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem
<table class="table">
<thead>
<tr>@TableHeader</tr>
</thead>
<tbody>
@foreach (var item in Items)
{
<tr @key="@item">@RowTemplate(item)</tr>
}
</tbody>
</table>
@code {
[Parameter]
public RenderFragment TableHeader { get; set; }
[Parameter]
public RenderFragment<TItem> RowTemplate { get; set; } = default;
[Parameter]
public IReadOnlyList<TItem> Items { get; set; } = default;
}
@typeparam TItem
<table class="table">
<thead>
<tr>@TableHeader</tr>
</thead>
<tbody>
@foreach (var item in Items)
{
<tr @key="@item">@RowTemplate(item)</tr>
}
</tbody>
</table>
@code {
[Parameter]
public RenderFragment TableHeader { get; set; }
[Parameter]
public RenderFragment<TItem> RowTemplate { get; set; } = default;
[Parameter]
public IReadOnlyList<TItem> Items { get; set; } = default;
}
Рассмотрим следующий Pets5
компонент (Pets5.razor
), демонстрирующий важность ключевых данных для сохранения связей модели. В компоненте каждая итерация добавления животного OnAfterRenderAsync
приводит к Blazor перебору TableTemplate
компонента.
Pets5.razor
:
@page "/pets-5"
<PageTitle>Pets 5</PageTitle>
<h1>Pets Example 5</h1>
<TableTemplate Items="pets" Context="pet">
<TableHeader>
<th>ID</th>
<th>Name</th>
</TableHeader>
<RowTemplate>
<td><input value="@pet.PetId" /></td>
<td><input value="@pet.Name" /></td>
</RowTemplate>
</TableTemplate>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Insert new pet every 5 seconds
if (pets.Count < 10)
{
await Task.Delay(5000);
pets.Insert(0, new Pet() { PetId = pets.Count + 1, Name = "<new pet>" });
StateHasChanged();
}
}
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-5"
<PageTitle>Pets 5</PageTitle>
<h1>Pets Example 5</h1>
<TableTemplate Items="pets" Context="pet">
<TableHeader>
<th>ID</th>
<th>Name</th>
</TableHeader>
<RowTemplate>
<td><input value="@pet.PetId" /></td>
<td><input value="@pet.Name" /></td>
</RowTemplate>
</TableTemplate>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Insert new pet every 5 seconds
if (pets.Count < 10)
{
await Task.Delay(5000);
pets.Insert(0, new Pet() { PetId = pets.Count + 1, Name = "<new pet>" });
StateHasChanged();
}
}
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-5"
<PageTitle>Pets 5</PageTitle>
<h1>Pets Example 5</h1>
<TableTemplate Items="pets" Context="pet">
<TableHeader>
<th>ID</th>
<th>Name</th>
</TableHeader>
<RowTemplate>
<td><input value="@pet.PetId" /></td>
<td><input value="@pet.Name" /></td>
</RowTemplate>
</TableTemplate>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Insert new pet every 5 seconds
if (pets.Count < 10)
{
await Task.Delay(5000);
pets.Insert(0, new Pet() { PetId = pets.Count + 1, Name = "<new pet>" });
StateHasChanged();
}
}
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-5"
<PageTitle>Pets 5</PageTitle>
<h1>Pets Example 5</h1>
<TableTemplate Items="pets" Context="pet">
<TableHeader>
<th>ID</th>
<th>Name</th>
</TableHeader>
<RowTemplate>
<td><input value="@pet.PetId" /></td>
<td><input value="@pet.Name" /></td>
</RowTemplate>
</TableTemplate>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Insert new pet every 5 seconds
if (pets.Count < 10)
{
await Task.Delay(5000);
pets.Insert(0, new Pet() { PetId = pets.Count + 1, Name = "<new pet>" });
StateHasChanged();
}
}
private class Pet
{
public int PetId { get; set; }
public string? Name { get; set; }
}
}
@page "/pets-5"
<h1>Pets Example 5</h1>
<TableTemplate Items="pets" Context="pet">
<TableHeader>
<th>ID</th>
<th>Name</th>
</TableHeader>
<RowTemplate>
<td><input value="@pet.PetId" /></td>
<td><input value="@pet.Name" /></td>
</RowTemplate>
</TableTemplate>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Insert new pet every 5 seconds
if (pets.Count < 10)
{
await Task.Delay(5000);
pets.Insert(0, new Pet() { PetId = pets.Count + 1, Name = "<new pet>" });
StateHasChanged();
}
}
private class Pet
{
public int PetId { get; set; }
public string Name { get; set; }
}
}
@page "/pets-5"
<h1>Pets Example 5</h1>
<TableTemplate Items="pets" Context="pet">
<TableHeader>
<th>ID</th>
<th>Name</th>
</TableHeader>
<RowTemplate>
<td><input value="@pet.PetId" /></td>
<td><input value="@pet.Name" /></td>
</RowTemplate>
</TableTemplate>
@code {
private List<Pet> pets = new()
{
new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
new Pet { PetId = 2, Name = "Salem Saberhagen" },
new Pet { PetId = 3, Name = "K-9" }
};
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Insert new pet every 5 seconds
if (pets.Count < 10)
{
await Task.Delay(5000);
pets.Insert(0, new Pet() { PetId = pets.Count + 1, Name = "<new pet>" });
StateHasChanged();
}
}
private class Pet
{
public int PetId { get; set; }
public string Name { get; set; }
}
}
В этой демонстрации можно выполнить следующие действия:
<input>
Выберите один из нескольких отрисованных строк таблицы.- Изучите поведение фокуса страницы, так как коллекция домашних животных автоматически растет.
Без использования @key
атрибута директивы в TableTemplate
компоненте фокус страницы остается на той же позиции индекса (строке) таблицы, что приводит к перемещению фокуса при каждом добавлении питомца. Чтобы продемонстрировать это, удалите @key
атрибут директивы и значение, перезапустите приложение и попытайтесь изменить значение поля при добавлении элементов.
Дополнительные ресурсы
ASP.NET Core