Événement
Championnats du monde Power BI DataViz
14 févr., 16 h - 31 mars, 16 h
Avec 4 chances d’entrer, vous pourriez gagner un package de conférence et le rendre à la Live Grand Finale à Las Vegas
En savoir plusCe navigateur n’est plus pris en charge.
Effectuez une mise à niveau vers Microsoft Edge pour tirer parti des dernières fonctionnalités, des mises à jour de sécurité et du support technique.
Note
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 9 de cet article.
Avertissement
Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la stratégie de support .NET et .NET Core. Pour la version actuelle, consultez la version .NET 9 de cet article.
Important
Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.
Pour la version actuelle, consultez la version .NET 9 de cet article.
Cet article explique comment utiliser l’attribut de directive @key
, pour la conservation des relations d’élément, de composant et de modèle, lors du rendu et lorsque les éléments ou composants changent par la suite.
Si, après le rendu d’une liste d’éléments ou de composants, les éléments ou composants changent, Blazor doit décider lesquels des éléments ou composants précédents peuvent être conservés et déterminer le mode de mappage entre les objets de modèle et ces éléments ou composants. Normalement, ce processus est automatique et suffisant pour le rendu général, mais il existe souvent des cas où le contrôle du processus à l’aide de l’attribut @key
de directive est nécessaire.
Considérez l’exemple suivant qui illustre un problème de mappage de collection résolu à l’aide de @key
.
Pour les composants suivants :
Details
reçoit du composant parent des données (Data
), qui sont affichées dans un élément <input>
. Tout élément <input>
affiché donné peut recevoir le focus de la page de l’utilisateur lorsqu’il sélectionne l’un des éléments <input>
.Details
. Toutes les trois secondes, une nouvelle personne est ajoutée à la collection.Cette démonstration vous permet de :
<input>
parmi plusieurs composants Details
rendus.Details.razor
:
<input value="@Data" />
@code {
[Parameter]
public string? Data { get; set; }
}
<input value="@Data" />
@code {
[Parameter]
public string? Data { get; set; }
}
<input value="@Data" />
@code {
[Parameter]
public string? Data { get; set; }
}
<input value="@Data" />
@code {
[Parameter]
public string? Data { get; set; }
}
<input value="@Data" />
@code {
[Parameter]
public string Data { get; set; }
}
<input value="@Data" />
@code {
[Parameter]
public string Data { get; set; }
}
Dans le composant parent suivant, chaque itération d’ajout d’une personne dans OnTimerCallback
oblige Blazor à reconstruire l’intégralité de la collection. Le focus de la page restant sur la même position d’index des éléments <input>
, le focus change chaque fois qu’une personne est ajoutée. Il n’est pas souhaitable de déplacer le focus hors de la sélection de l’utilisateur. Après avoir démontré le mauvais comportement avec le composant suivant, l’attribut de directive @key
est utilisé pour améliorer l’expérience de l’utilisateur.
People.razor
:
@page "/people"
@using System.Timers
@implements IDisposable
<PageTitle>People</PageTitle>
<h1>People Example</h1>
@foreach (var person in people)
{
<Details Data="@person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string? Data { get; set; }
}
}
People.razor
:
@page "/people"
@using System.Timers
@implements IDisposable
<PageTitle>People</PageTitle>
<h1>People Example</h1>
@foreach (var person in people)
{
<Details Data="@person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string? Data { get; set; }
}
}
PeopleExample.razor
:
@page "/people-example"
@using System.Timers
@implements IDisposable
@foreach (var person in people)
{
<Details Data="person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string? Data { get; set; }
}
}
PeopleExample.razor
:
@page "/people-example"
@using System.Timers
@implements IDisposable
@foreach (var person in people)
{
<Details Data="person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string? Data { get; set; }
}
}
PeopleExample.razor
:
@page "/people-example"
@using System.Timers
@implements IDisposable
@foreach (var person in people)
{
<Details Data="person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string Data { get; set; }
}
}
PeopleExample.razor
:
@page "/people-example"
@using System.Timers
@implements IDisposable
@foreach (var person in people)
{
<Details Data="person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new List<Person>()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string Data { get; set; }
}
}
Le contenu de la collection people
change quand des entrées sont insérées, supprimées ou réorganisées. La regénération du rendu peut entraîner des différences de comportement visibles. Par exemple, chaque fois qu’une personne est insérée dans la collection people
, le focus de l’utilisateur est perdu.
Le processus de mappage d’éléments ou de composants à une collection peut être contrôlé avec l’attribut de directive @key
. L’utilisation de @key
garantit la conservation d’éléments ou de composants en fonction de la valeur de la clé. Si le composant Details
dans l’exemple précédent est indexé sur l’élément person
, Blazor ignore le nouveau rendu des composants Details
qui n’ont pas changé.
Pour modifier le composant parent afin d’utiliser l’attribut de directive @key
avec la collection people
, mettez à jour l’élément <Details>
comme suit :
<Details @key="person" Data="@person.Data" />
Quand la collection people
change, l’association entre les instances de Details
et de person
est conservée. Quand Person
est inséré au début de la collection, une nouvelle instance de Details
est insérée à la position correspondante. Les autres instances sont inchangées. Le focus de l’utilisateur n’est donc pas perdu à mesure que des personnes sont ajoutées à la collection.
D’autres mises à jour de collection présentent le même comportement quand l’attribut de directive @key
est utilisé :
Important
Les clés sont locales à chaque composant ou élément conteneur. Les clés ne sont pas comparées globalement dans le document.
En règle générale, il est judicieux d’utiliser @key
chaque fois qu’une liste est rendue (par exemple, dans un bloc foreach
) et qu’une valeur appropriée existe pour définir @key
.
Vous pouvez également utiliser @key
pour conserver une sous-arborescence d’éléments ou de composants quand un objet ne change pas, comme le montrent les exemples suivants.
Exemple 1 :
<li @key="person">
<input value="@person.Data" />
</li>
Exemple 2 :
<div @key="person">
@* other HTML elements *@
</div>
Si une instance de person
change, la directive d’attribut @key
force Blazor à :
<li>
ou de <div>
et les descendants.Cela permet de garantir qu’aucun état de l’interface utilisateur n’est préservé quand la collection change dans une sous-arborescence.
La directive d’attribut @key
est délimitée à ses propres frères au sein de son parent.
Prenons l'exemple suivant. Les clés first
et second
sont comparées l’une à l’autre dans la même étendue de l’élément <div>
externe :
<div>
<div @key="first">...</div>
<div @key="second">...</div>
</div>
L’exemple suivant illustre les clés first
et second
dans leurs propres étendues, sans aucun rapport entre elles et sans aucune influence de l’une sur l’autre. Chaque étendue @key
s’applique uniquement à son élément <div>
parent, et non à travers les éléments <div>
parents :
<div>
<div @key="first">...</div>
</div>
<div>
<div @key="second">...</div>
</div>
Pour le composant Details
présenté précédemment, les exemples suivants génèrent le rendu des données person
dans la même étendue @key
et illustrent des cas d’usage standard pour @key
:
<div>
@foreach (var person in people)
{
<Details @key="person" Data="@person.Data" />
}
</div>
@foreach (var person in people)
{
<div @key="person">
<Details Data="@person.Data" />
</div>
}
<ol>
@foreach (var person in people)
{
<li @key="person">
<Details Data="@person.Data" />
</li>
}
</ol>
Les exemples suivants étendent uniquement @key
à l’élément <div>
ou <li>
qui entoure chaque instance de composant Details
. Les données person
pour chaque membre de la collection people
ne sont donc pas indexées sur chaque instance de person
à travers les composants Details
rendus. Évitez les modèles suivants quand vous utilisez @key
:
@foreach (var person in people)
{
<div>
<Details @key="person" Data="@person.Data" />
</div>
}
<ol>
@foreach (var person in people)
{
<li>
<Details @key="person" Data="@person.Data" />
</li>
}
</ol>
Le rendu avec @key
entraîne un coût en termes de performances. Ce coût en termes de performances n’est pas important, mais spécifiez uniquement @key
si la conservation de l’élément ou du composant profite à l’application.
Même si @key
n’est pas utilisé, Blazor conserve autant que possible les instances des composants et des éléments enfants. Le seul avantage à utiliser @key
est de contrôler la façon dont les instances de modèle sont mappées aux instances de composant conservées afin d’éviter que Blazor ne sélectionne le mappage.
En règle générale, il est judicieux de fournir l’une des valeurs suivantes pour @key
:
Person
(person
) a été utilisée dans l’exemple précédent. Cela garantit une conservation basée sur l’égalité des références d’objet.int
, string
ou Guid
.Vérifiez que les valeurs utilisées pour @key
ne sont pas en conflit. Si des valeurs en conflit sont détectées dans le même élément parent, Blazor lève une exception car il ne peut pas mapper de manière déterministe les anciens éléments ou composants aux nouveaux. Utilisez uniquement des valeurs distinctes, comme des instances d’objet ou des valeurs de clé primaire.
Commentaires sur ASP.NET Core
ASP.NET Core est un projet open source. Sélectionnez un lien pour fournir des commentaires :
Événement
Championnats du monde Power BI DataViz
14 févr., 16 h - 31 mars, 16 h
Avec 4 chances d’entrer, vous pourriez gagner un package de conférence et le rendre à la Live Grand Finale à Las Vegas
En savoir plusFormation
Module
Interagir avec des données dans des applications web Blazor - Training
Découvrez comment créer une interface utilisateur graphique dans une application web Blazor en créant et en assemblant des composants Blazor. Accédez aux données et partagez-les pour les afficher sur plusieurs pages dans votre application.
Documentation
Évitez d'écraser les paramètres dans ASP.NET Core Blazor
Apprenez à éviter l'écrasement des paramètres dans les applications Blazor lors du rendu.
Projection d’attributs et paramètres arbitraires ASP.NET Core Blazor
Découvrez comment les composants peuvent capturer et afficher des attributs supplémentaires, en plus des paramètres déclarés du composant.
Rendu de composants ASP.NET Core Razor
Découvrez le rendu des composants Razor dans les applications ASP.NET Core Blazor, notamment quand déclencher manuellement un composant à afficher.