Composants de vue dans ASP.NET Core

Par Rick Anderson

Composants de vue

Les composants de vue sont similaires aux vues partielles, mais ils sont beaucoup plus puissants. Les composants de vue n’utilisent pas de liaison de modèle, ils dépendent des données transmises lors de l’appel du composant de vue. Cet article a été écrit en utilisant des contrôleurs et des vues, mais les composants de vue fonctionnent avec Razor Pages.

Un composant de vue a les caractéristiques suivantes :

  • Il effectue le rendu d’un bloc de code au lieu d’une réponse entière.
  • Il garantit la même « séparation des préoccupations » et offre les mêmes avantages de testabilité qu’entre un contrôleur et une vue.
  • Il peut avoir des paramètres et une logique métier.
  • Il est généralement appelé à partir d’une page de disposition.

Les composants de vue se destinent à toute logique de rendu réutilisable qui est trop complexe pour une vue partielle, par exemple :

  • Menus de navigation dynamiques
  • Nuage de mots clés, pour l’interrogation de la base de données
  • Panneau de connexion
  • Panier d’achat
  • Articles récemment publiés
  • Contenu de barre latérale sur un blog
  • Panneau de connexion affiché dans chaque page avec les liens de connexion ou de déconnexion, selon l’état de connexion de l’utilisateur

Un composant de vue est constitué de deux parties :

  • La classe, généralement dérivée de ViewComponent
  • Le résultat qu’il retourne, généralement une vue.

Comme les contrôleurs, un composant de vue peut être un OCT, mais la plupart des développeurs tirent parti des méthodes et des propriétés dérivées de ViewComponent.

Lorsque vous vérifiez si des composants de vue répondent aux spécifications d’une application, utilisez plutôt des composants Razor. Les composants Razor combinent également du balisage avec le code C# pour produire des unités d’interface utilisateur réutilisables. Les composants Razor ont été pensés pour favoriser la productivité des développeurs lorsqu’ils fournissent une logique et une composition d’interface utilisateur côté client. Pour plus d’informations, consultez Composants ASP.NET Core Razor. Pour savoir comment incorporer des composants Razor dans une application MVC ou Razor Pages, consultez Intégrer des composants ASP.NET CoreRazor dans des applications ASP.NET Core.

Créer un composant de vue

Cette section présente les exigences générales relatives à la création d’un composant de vue. Plus loin dans cet article, nous décrirons chaque étape en détail et nous créerons un composant de vue.

Classe de composant de vue

Vous pouvez créer une classe de composant de vue à l’aide d’une des méthodes suivantes :

  • En dérivant de ViewComponent
  • En décorant une classe avec l’attribut [ViewComponent] ou en dérivant la classe d’une classe définie avec l’attribut [ViewComponent]
  • En créant une classe dont le nom se termine par le suffixe ViewComponent

Comme les contrôleurs, les composants de vue doivent être des classes publiques, non imbriquées et non abstraites. Le nom du composant de vue correspond au nom de la classe sans le suffixe ViewComponent. Il peut également être spécifié explicitement à l’aide de la propriété Name.

Une classe de composant de vue :

  • Prend en charge l’injection de dépendances de constructeur
  • Ne participe pas au cycle de vie du contrôleur. Il n’est donc pas possible d’utiliser de filtres dans un composant de vue

Pour éviter qu’une classe qui possède un suffixe ViewComponent insensible à la casse soit traitée comme un composant de vue, décorez la classe avec l’attribut [NonViewComponent] :

using Microsoft.AspNetCore.Mvc;

[NonViewComponent]
public class ReviewComponent
{
    public string Status(string name) => JobStatus.GetCurrentStatus(name);
}

Méthodes d’un composant de vue

Un composant de vue définit sa logique dans :

  • Une méthode InvokeAsync qui retourne Task<IViewComponentResult>.
  • Une méthode synchrone Invoke qui retourne un IViewComponentResult.

Les paramètres sont fournis directement en réponse à l’appel du composant de vue ; ils ne proviennent pas de la liaison de données. Un composant de vue ne traite jamais une requête directement. En règle générale, un composant de vue initialise un modèle et le passe à une vue en appelant la méthode View. En résumé, les méthodes d’un composant de vue :

  • Définissent une InvokeAsync méthode qui retourne un Task<IViewComponentResult> ou une méthode Invoke synchrone qui retourne un IViewComponentResult.
  • Initialise généralement un modèle et le passe à une vue en appelant la méthode ViewComponent.View.
  • Les paramètres proviennent de la méthode appelante, et non pas de HTTP. Il n’y a pas de liaison de modèle.
  • Ne sont pas accessibles directement en tant que point de terminaison HTTP. Elles sont généralement appelées dans une vue. Un composant de vue ne traite jamais une requête.
  • Sont surchargées sur la signature, plutôt que sur des détails de la requête HTTP en cours.

Chemin de recherche de la vue

Le Runtime recherche la vue dans les chemins suivants :

  • /Views/{Controller Name}/Components/{View Component Name}/{View Name}
  • /Views/Shared/Components/{View Component Name}/{View Name}
  • /Pages/Shared/Components/{View Component Name}/{View Name}
  • /Areas/{Area Name}/Views/Shared/Components/{View Component Name}/{View Name}

Le chemin de recherche s’applique aux projets utilisant contrôleurs + vues et Razor Pages.

Le nom de vue par défaut pour un composant de vue est Default, ce qui signifie que les fichiers de vue se nomment normalement Default.cshtml. Il est possible de spécifier un nom de vue différent lors de la création du résultat du composant de vue ou de l’appel de la méthode View.

Nous vous recommandons de nommer le fichier de vue Default.cshtml et d’utiliser le chemin Views/Shared/Components/{View Component Name}/{View Name}. Le composant de vue PriorityList utilisé dans cet exemple utilise Views/Shared/Components/PriorityList/Default.cshtml pour la vue.

Personnaliser le chemin de recherche de vues

Pour personnaliser le chemin de recherche de vues, modifiez la collection ViewLocationFormats de Razor. Par exemple, pour rechercher des vues dans le chemin /Components/{View Component Name}/{View Name}, ajoutez un nouvel élément à la collection :

using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews()
    .AddRazorOptions(options =>
    {
        options.ViewLocationFormats.Add("/{0}.cshtml");
    });

builder.Services.AddDbContext<ToDoContext>(options =>
        options.UseInMemoryDatabase("db"));

var app = builder.Build();

// Remaining code removed for brevity.

Dans le code précédent, l’espace réservé {0} représente le chemin Components/{View Component Name}/{View Name}.

Appeler un composant de vue

Pour utiliser le composant de vue, appelez-le dans une vue, comme ci-dessous :

@await Component.InvokeAsync("Name of view component",
                             {Anonymous Type Containing Parameters})

Les paramètres sont passés à la méthode InvokeAsync. Le composant de vue PriorityList développé dans cet article est appelé à partir du fichier de vue Views/ToDo/Index.cshtml. Dans le code suivant, la méthode InvokeAsync est appelée avec deux paramètres :

</table>

<div>
    Maximum Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync("PriorityList",
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Appeler un composant de vue en tant que Tag Helper

Un composant de vue peut être appelé en tant que Tag Helper :

<div>
       Maxium Priority: @ViewData["maxPriority"] <br />
       Is Complete:  @ViewData["isDone"]
    @{
        int maxPriority = Convert.ToInt32(ViewData["maxPriority"]);
        bool isDone = Convert.ToBoolean(ViewData["isDone"]);
    }
    <vc:priority-list max-priority=maxPriority is-done=isDone>
    </vc:priority-list>
</div>

Les paramètres de méthode et de classe de casse Pascal pour les Tag Helpers sont convertis en casse kebab (mots séparés par des tirets). Le Tag Helper utilisé pour appeler un composant de vue contient l’élément <vc></vc>. Le composant de vue est spécifié de la façon suivante :

<vc:[view-component-name]
  parameter1="parameter1 value"
  parameter2="parameter2 value">
</vc:[view-component-name]>

Pour utiliser un composant de vue en tant que Tag Helper, inscrivez l’assembly contenant le composant de vue à l’aide de la directive @addTagHelper. Si le composant de vue se trouve dans un assembly appelé MyWebApp, ajoutez la directive suivante au fichier _ViewImports.cshtml :

@addTagHelper *, MyWebApp

Il est possible d’inscrire un composant de vue en tant que Tag Helper dans n’importe quel fichier qui fait référence à ce composant. Pour plus d’informations sur l’inscription des Tag Helpers, consultez Gestion de l’étendue des Tag Helpers.

Méthode InvokeAsync utilisée dans ce didacticiel :

@await Component.InvokeAsync("PriorityList",
                 new { 
                     maxPriority =  ViewData["maxPriority"],
                     isDone = ViewData["isDone"]  }
                 )

Dans le balisage précédent, le composant de vue PriorityList devient priority-list. Les paramètres sont passés au composant de vue sous forme d’attributs dans la casse kebab.

Appeler un composant de vue directement à partir d’un contrôleur

Les composants de vue sont généralement appelés depuis une vue, mais ils peuvent aussi être appelés directement à partir d’une méthode de contrôleur. Même si les composants de vue ne définissent pas de points de terminaison comme les contrôleurs, il est possible d’implémenter une action de contrôleur qui retourne le contenu d’un ViewComponentResult.

Dans l’exemple suivant, le composant de vue est appelé directement à partir du contrôleur :

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

Créer un composant de vue de base

Téléchargez, générez et testez le code de démarrage. Il s’agit d’un projet de base avec un contrôleur ToDo qui affiche une liste de tâches ToDo.

List of ToDos

Mettre à jour le contrôleur pour transmettre l’état de priorité et d’achèvement

Mettez à jour la méthode Index pour utiliser les paramètres d’état de priorité et d’achèvement :

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.Controllers;
public class ToDoController : Controller
{
    private readonly ToDoContext _ToDoContext;

    public ToDoController(ToDoContext context)
    {
        _ToDoContext = context;
        _ToDoContext.Database.EnsureCreated();
    }

    public IActionResult Index(int maxPriority = 2, bool isDone = false)
    {
        var model = _ToDoContext!.ToDo!.ToList();
        ViewData["maxPriority"] = maxPriority;
        ViewData["isDone"] = isDone;
        return View(model);
    }

Ajouter une classe ViewComponent

Ajoutez une classe ViewComponent à ViewComponents/PriorityListViewComponent.cs :

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityListViewComponent : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityListViewComponent(ToDoContext context) => db = context;

    public async Task<IViewComponentResult> InvokeAsync(
                                            int maxPriority, bool isDone)
    {
        var items = await GetItemsAsync(maxPriority, isDone);
        return View(items);
    }

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

Remarques sur le code :

  • Les classes ViewComponent peuvent être ajoutées à n’importe quel dossier dans le projet.

  • Étant donné que le nom de classe PriorityListViewComponent se termine par le suffixe ViewComponent, le runtime utilise la chaîne PriorityList pour référencer le composant de classe à partir d’une vue.

  • L’attribut [ViewComponent] peut changer le nom utilisé pour faire référence à un composant de vue. Par exemple, la classe peut avoir été nommée XYZ avec l’attribut [ViewComponent] suivant :

    [ViewComponent(Name = "PriorityList")]
       public class XYZ : ViewComponent
    
  • L’attribut [ViewComponent] présent dans le code précédent indique au sélecteur de composant de vue d’utiliser :

    • Le nom PriorityList lors de la recherche des vues associées au composant
    • La chaîne « PriorityList » lors du référencement du composant de classe à partir d’une vue.
  • Le composant utilise l’injection de dépendances pour rendre le contexte de données disponible.

  • InvokeAsync expose une méthode qui peut être appelée à partir d’une vue et qui peut prendre un nombre arbitraire d’arguments.

  • La méthode InvokeAsync retourne l’ensemble des tâches ToDo qui correspondent aux paramètres isDone et maxPriority spécifiés.

Créer la vue Razor du composant de vue

  • Créez le dossier Views/Shared/Components. Ce dossier doit être nommé Components.

  • Créez le dossier Views/Shared/Components/PriorityList. Ce nom de dossier doit correspondre au nom de la classe de composant de vue ou au nom de la classe moins le suffixe. Si l’attribut ViewComponent est utilisé, le nom de la classe doit correspondre à la désignation de l’attribut.

  • Créez une vue RazorViews/Shared/Components/PriorityList/Default.cshtml :

    @model IEnumerable<ViewComponentSample.Models.TodoItem>
    
    <h3>Priority Items</h3>
    <ul>
        @foreach (var todo in Model)
        {
            <li>@todo.Name</li>
        }
    </ul>
    

    La vue Razor prend une liste de tâches TodoItem et les affiche. Si la méthode InvokeAsync de composant de vue ne passe pas le nom de la vue, le nom de vue Default est utilisé par convention. Pour substituer le style par défaut d’un contrôleur spécifique, ajoutez une vue dans le dossier des vues du contrôleur (par exemple, Views/ToDo/Components/PriorityList/Default.cshtml).

    Si le composant de vue est propre au contrôleur, il peut être ajouté au dossier spécifique du contrôleur. Par exemple, Views/ToDo/Components/PriorityList/Default.cshtml est propre au contrôleur.

  • Ajoutez une balise div contenant un appel au composant de liste des priorités au bas du fichier Views/ToDo/index.cshtml :

    </table>
    
    <div>
        Maximum Priority: @ViewData["maxPriority"] <br />
        Is Complete:  @ViewData["isDone"]
        @await Component.InvokeAsync("PriorityList",
                         new { 
                             maxPriority =  ViewData["maxPriority"],
                             isDone = ViewData["isDone"]  }
                         )
    </div>
    

Le balisage @await Component.InvokeAsync montre la syntaxe utilisée pour appeler un composant de vue. Le premier argument est le nom du composant à appeler. Les paramètres suivants sont passés au composant. InvokeAsync peut prendre un nombre arbitraire d’arguments.

Tester l'application. L’image suivante montre la liste des tâches ToDo et les tâches prioritaires :

todo list and priority items

Le composant de vue peut être appelé directement à partir du contrôleur :

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

priority items from IndexVC action

Spécifier un nom de composant de vue

Un composant de vue complexe peut avoir besoin de spécifier une autre vue que la vue par défaut dans certaines conditions. Le code suivant montre comment spécifier la vue « PVC » à partir de la méthode InvokeAsync. Mettez à jour la méthode InvokeAsync dans la classe PriorityListViewComponent.

public async Task<IViewComponentResult> InvokeAsync(
                                           int maxPriority, bool isDone)
{
    string MyView = "Default";
    // If asking for all completed tasks, render with the "PVC" view.
    if (maxPriority > 3 && isDone == true)
    {
        MyView = "PVC";
    }
    var items = await GetItemsAsync(maxPriority, isDone);
    return View(MyView, items);
}

Copiez le fichier Views/Shared/Components/PriorityList/Default.cshtml dans une vue nommée Views/Shared/Components/PriorityList/PVC.cshtml. Ajoutez un titre pour indiquer que la vue PVC est utilisée.

@model IEnumerable<ViewComponentSample.Models.TodoItem>

<h2> PVC Named Priority Component View</h2>
<h4>@ViewBag.PriorityMessage</h4>
<ul>
    @foreach (var todo in Model)
    {
        <li>@todo.Name</li>
    }
</ul>

Exécutez l’application et vérifiez la vue PVC.

Priority View Component

Si la vue PVC ne s’affiche pas, vérifiez que le composant de vue de priorité 4 ou plus est appelé.

Vérifier le chemin de la vue

  • Changez le paramètre de priorité à une priorité inférieure ou égale à 3 pour empêcher l’affichage de la vue des priorités.

  • Renommez temporairement Views/ToDo/Components/PriorityList/Default.cshtml en 1Default.cshtml.

  • Testez l’application, l’erreur suivante se produit :

    An unhandled exception occurred while processing the request.
    InvalidOperationException: The view 'Components/PriorityList/Default' wasn't found. The following locations were searched:
    /Views/ToDo/Components/PriorityList/Default.cshtml
    /Views/Shared/Components/PriorityList/Default.cshtml
    
  • Copiez Views/ToDo/Components/PriorityList/1Default.cshtml dans Views/Shared/Components/PriorityList/Default.cshtml.

  • Ajoutez du balisage à la vue ToDo Shared du composant de vue pour indiquer que la vue provient du dossier Shared.

  • Testez la vue de composant Shared.

ToDo output with Shared component view

Éviter les chaînes codées en dur

Par mesure de sécurité, au moment de la compilation, remplacez le nom du composant de vue codé en dur par le nom de la classe. Mettez à jour le fichier PriorityListViewComponent.cs pour éviter qu’il utilise le suffixe « ViewComponent » :

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityList : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityList(ToDoContext context)
    {
        db = context;
    }

    public async Task<IViewComponentResult> InvokeAsync(
                                               int maxPriority, bool isDone)
    {
        var items = await GetItemsAsync(maxPriority, isDone);
        return View(items);
    }

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

Le fichier de vue :

</table>

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Une surcharge de la méthode Component.InvokeAsync qui prend un type CLR utilise l’opérateur typeof :

</table>

<div>
    Testing typeof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(typeof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Effectuer un travail synchrone

Le framework gère l’appel d’une méthode Invoke synchrone si le travail asynchrone n’est pas nécessaire. La méthode suivante crée un composant de vue Invoke synchrone :

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityListSync : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityListSync(ToDoContext context)
        {
            db = context;
        }

        public IViewComponentResult Invoke(int maxPriority, bool isDone)
        {
 
            var x = db!.ToDo!.Where(x => x.IsDone == isDone &&
                                  x.Priority <= maxPriority).ToList();
            return View(x);
        }
    }
}

Le fichier Razor du composant de vue :

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityListSync),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Le composant de vue est appelé dans un fichier Razor (par exemple, Views/Home/Index.cshtml) en utilisant l’une des approches suivantes :

Pour utiliser l’approche IViewComponentHelper, appelez Component.InvokeAsync :

@await Component.InvokeAsync(nameof(PriorityList),
                             new { maxPriority = 4, isDone = true })

Pour utiliser le Tag Helper, inscrivez l’assembly contenant le composant de vue à l’aide de la directive @addTagHelper (le composant de vue se trouve dans un assembly appelé MyWebApp) :

@addTagHelper *, MyWebApp

Utilisez le Tag Helper du composant de vue dans le fichier de balisage Razor :

<vc:priority-list max-priority="999" is-done="false">
</vc:priority-list>

La signature de méthode de PriorityList.Invoke est synchrone, mais Razor recherche et appelle la méthode avec Component.InvokeAsync dans le fichier de balisage.

Ressources supplémentaires

Composants de vue

Les composants de vue sont similaires aux vues partielles, mais ils sont beaucoup plus puissants. Les composants de vue n’utilisent pas de liaison de modèle, ils dépendent des données transmises lors de l’appel du composant de vue. Cet article a été écrit en utilisant des contrôleurs et des vues, mais les composants de vue fonctionnent avec Razor Pages.

Un composant de vue a les caractéristiques suivantes :

  • Il effectue le rendu d’un bloc de code au lieu d’une réponse entière.
  • Il garantit la même « séparation des préoccupations » et offre les mêmes avantages de testabilité qu’entre un contrôleur et une vue.
  • Il peut avoir des paramètres et une logique métier.
  • Il est généralement appelé à partir d’une page de disposition.

Les composants de vue se destinent à toute logique de rendu réutilisable qui est trop complexe pour une vue partielle, par exemple :

  • Menus de navigation dynamiques
  • Nuage de mots clés, pour l’interrogation de la base de données
  • Panneau de connexion
  • Panier d’achat
  • Articles récemment publiés
  • Contenu de barre latérale sur un blog
  • Panneau de connexion affiché dans chaque page avec les liens de connexion ou de déconnexion, selon l’état de connexion de l’utilisateur

Un composant de vue est constitué de deux parties :

  • La classe, généralement dérivée de ViewComponent
  • Le résultat qu’il retourne, généralement une vue.

Comme les contrôleurs, un composant de vue peut être un OCT, mais la plupart des développeurs tirent parti des méthodes et des propriétés dérivées de ViewComponent.

Lorsque vous vérifiez si des composants de vue répondent aux spécifications d’une application, utilisez plutôt des composants Razor. Les composants Razor combinent également du balisage avec le code C# pour produire des unités d’interface utilisateur réutilisables. Les composants Razor ont été pensés pour favoriser la productivité des développeurs lorsqu’ils fournissent une logique et une composition d’interface utilisateur côté client. Pour plus d’informations, consultez Composants ASP.NET Core Razor. Pour savoir comment incorporer des composants Razor dans une application MVC ou Razor Pages, consultez Effectuer le prérendu et l’intégration de composants Razor ASP.NET Core.

Créer un composant de vue

Cette section présente les exigences générales relatives à la création d’un composant de vue. Plus loin dans cet article, nous décrirons chaque étape en détail et nous créerons un composant de vue.

Classe de composant de vue

Vous pouvez créer une classe de composant de vue à l’aide d’une des méthodes suivantes :

  • En dérivant de ViewComponent
  • En décorant une classe avec l’attribut [ViewComponent] ou en dérivant la classe d’une classe définie avec l’attribut [ViewComponent]
  • En créant une classe dont le nom se termine par le suffixe ViewComponent

Comme les contrôleurs, les composants de vue doivent être des classes publiques, non imbriquées et non abstraites. Le nom du composant de vue correspond au nom de la classe sans le suffixe ViewComponent. Il peut également être spécifié explicitement à l’aide de la propriété Name.

Une classe de composant de vue :

  • Prend en charge l’injection de dépendances de constructeur
  • Ne participe pas au cycle de vie du contrôleur. Il n’est donc pas possible d’utiliser de filtres dans un composant de vue

Pour éviter qu’une classe qui possède un suffixe ViewComponent insensible à la casse soit traitée comme un composant de vue, décorez la classe avec l’attribut [NonViewComponent] :

using Microsoft.AspNetCore.Mvc;

[NonViewComponent]
public class ReviewComponent
{
    public string Status(string name) => JobStatus.GetCurrentStatus(name);
}

Méthodes d’un composant de vue

Un composant de vue définit sa logique dans :

  • Une méthode InvokeAsync qui retourne Task<IViewComponentResult>.
  • Une méthode synchrone Invoke qui retourne un IViewComponentResult.

Les paramètres sont fournis directement en réponse à l’appel du composant de vue ; ils ne proviennent pas de la liaison de données. Un composant de vue ne traite jamais une requête directement. En règle générale, un composant de vue initialise un modèle et le passe à une vue en appelant la méthode View. En résumé, les méthodes d’un composant de vue :

  • Définissent une InvokeAsync méthode qui retourne un Task<IViewComponentResult> ou une méthode Invoke synchrone qui retourne un IViewComponentResult.
  • Initialise généralement un modèle et le passe à une vue en appelant la méthode ViewComponent.View.
  • Les paramètres proviennent de la méthode appelante, et non pas de HTTP. Il n’y a pas de liaison de modèle.
  • Ne sont pas accessibles directement en tant que point de terminaison HTTP. Elles sont généralement appelées dans une vue. Un composant de vue ne traite jamais une requête.
  • Sont surchargées sur la signature, plutôt que sur des détails de la requête HTTP en cours.

Chemin de recherche de la vue

Le Runtime recherche la vue dans les chemins suivants :

  • /Views/{Controller Name}/Components/{View Component Name}/{View Name}
  • /Views/Shared/Components/{View Component Name}/{View Name}
  • /Pages/Shared/Components/{View Component Name}/{View Name}
  • /Areas/{Area Name}/Views/Shared/Components/{View Component Name}/{View Name}

Le chemin de recherche s’applique aux projets utilisant contrôleurs + vues et Razor Pages.

Le nom de vue par défaut pour un composant de vue est Default, ce qui signifie que les fichiers de vue se nomment normalement Default.cshtml. Il est possible de spécifier un nom de vue différent lors de la création du résultat du composant de vue ou de l’appel de la méthode View.

Nous vous recommandons de nommer le fichier de vue Default.cshtml et d’utiliser le chemin Views/Shared/Components/{View Component Name}/{View Name}. Le composant de vue PriorityList utilisé dans cet exemple utilise Views/Shared/Components/PriorityList/Default.cshtml pour la vue.

Personnaliser le chemin de recherche de vues

Pour personnaliser le chemin de recherche de vues, modifiez la collection ViewLocationFormats de Razor. Par exemple, pour rechercher des vues dans le chemin /Components/{View Component Name}/{View Name}, ajoutez un nouvel élément à la collection :

using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews()
    .AddRazorOptions(options =>
    {
        options.ViewLocationFormats.Add("/{0}.cshtml");
    });

builder.Services.AddDbContext<ToDoContext>(options =>
        options.UseInMemoryDatabase("db"));

var app = builder.Build();

// Remaining code removed for brevity.

Dans le code précédent, l’espace réservé {0} représente le chemin Components/{View Component Name}/{View Name}.

Appeler un composant de vue

Pour utiliser le composant de vue, appelez-le dans une vue, comme ci-dessous :

@await Component.InvokeAsync("Name of view component",
                             {Anonymous Type Containing Parameters})

Les paramètres sont passés à la méthode InvokeAsync. Le composant de vue PriorityList développé dans cet article est appelé à partir du fichier de vue Views/ToDo/Index.cshtml. Dans le code suivant, la méthode InvokeAsync est appelée avec deux paramètres :

</table>

<div>
    Maximum Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync("PriorityList",
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Appeler un composant de vue en tant que Tag Helper

Un composant de vue peut être appelé en tant que Tag Helper :

<div>
       Maxium Priority: @ViewData["maxPriority"] <br />
       Is Complete:  @ViewData["isDone"]
    @{
        int maxPriority = Convert.ToInt32(ViewData["maxPriority"]);
        bool isDone = Convert.ToBoolean(ViewData["isDone"]);
    }
    <vc:priority-list max-priority=maxPriority is-done=isDone>
    </vc:priority-list>
</div>

Les paramètres de méthode et de classe de casse Pascal pour les Tag Helpers sont convertis en casse kebab (mots séparés par des tirets). Le Tag Helper utilisé pour appeler un composant de vue contient l’élément <vc></vc>. Le composant de vue est spécifié de la façon suivante :

<vc:[view-component-name]
  parameter1="parameter1 value"
  parameter2="parameter2 value">
</vc:[view-component-name]>

Pour utiliser un composant de vue en tant que Tag Helper, inscrivez l’assembly contenant le composant de vue à l’aide de la directive @addTagHelper. Si le composant de vue se trouve dans un assembly appelé MyWebApp, ajoutez la directive suivante au fichier _ViewImports.cshtml :

@addTagHelper *, MyWebApp

Il est possible d’inscrire un composant de vue en tant que Tag Helper dans n’importe quel fichier qui fait référence à ce composant. Pour plus d’informations sur l’inscription des Tag Helpers, consultez Gestion de l’étendue des Tag Helpers.

Méthode InvokeAsync utilisée dans ce didacticiel :

@await Component.InvokeAsync("PriorityList",
                 new { 
                     maxPriority =  ViewData["maxPriority"],
                     isDone = ViewData["isDone"]  }
                 )

Dans le balisage précédent, le composant de vue PriorityList devient priority-list. Les paramètres sont passés au composant de vue sous forme d’attributs dans la casse kebab.

Appeler un composant de vue directement à partir d’un contrôleur

Les composants de vue sont généralement appelés depuis une vue, mais ils peuvent aussi être appelés directement à partir d’une méthode de contrôleur. Même si les composants de vue ne définissent pas de points de terminaison comme les contrôleurs, il est possible d’implémenter une action de contrôleur qui retourne le contenu d’un ViewComponentResult.

Dans l’exemple suivant, le composant de vue est appelé directement à partir du contrôleur :

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

Créer un composant de vue de base

Téléchargez, générez et testez le code de démarrage. Il s’agit d’un projet de base avec un contrôleur ToDo qui affiche une liste de tâches ToDo.

List of ToDos

Mettre à jour le contrôleur pour transmettre l’état de priorité et d’achèvement

Mettez à jour la méthode Index pour utiliser les paramètres d’état de priorité et d’achèvement :

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.Controllers;
public class ToDoController : Controller
{
    private readonly ToDoContext _ToDoContext;

    public ToDoController(ToDoContext context)
    {
        _ToDoContext = context;
        _ToDoContext.Database.EnsureCreated();
    }

    public IActionResult Index(int maxPriority = 2, bool isDone = false)
    {
        var model = _ToDoContext!.ToDo!.ToList();
        ViewData["maxPriority"] = maxPriority;
        ViewData["isDone"] = isDone;
        return View(model);
    }

Ajouter une classe ViewComponent

Ajoutez une classe ViewComponent à ViewComponents/PriorityListViewComponent.cs :

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityListViewComponent : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityListViewComponent(ToDoContext context) => db = context;

    public async Task<IViewComponentResult> InvokeAsync(
                                            int maxPriority, bool isDone)
    {
        var items = await GetItemsAsync(maxPriority, isDone);
        return View(items);
    }

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

Remarques sur le code :

  • Les classes ViewComponent peuvent être ajoutées à n’importe quel dossier dans le projet.

  • Étant donné que le nom de classe PriorityListViewComponent se termine par le suffixe ViewComponent, le runtime utilise la chaîne PriorityList pour référencer le composant de classe à partir d’une vue.

  • L’attribut [ViewComponent] peut changer le nom utilisé pour faire référence à un composant de vue. Par exemple, la classe peut avoir été nommée XYZ avec l’attribut [ViewComponent] suivant :

    [ViewComponent(Name = "PriorityList")]
       public class XYZ : ViewComponent
    
  • L’attribut [ViewComponent] présent dans le code précédent indique au sélecteur de composant de vue d’utiliser :

    • Le nom PriorityList lors de la recherche des vues associées au composant
    • La chaîne « PriorityList » lors du référencement du composant de classe à partir d’une vue.
  • Le composant utilise l’injection de dépendances pour rendre le contexte de données disponible.

  • InvokeAsync expose une méthode qui peut être appelée à partir d’une vue et qui peut prendre un nombre arbitraire d’arguments.

  • La méthode InvokeAsync retourne l’ensemble des tâches ToDo qui correspondent aux paramètres isDone et maxPriority spécifiés.

Créer la vue Razor du composant de vue

  • Créez le dossier Views/Shared/Components. Ce dossier doit être nommé Components.

  • Créez le dossier Views/Shared/Components/PriorityList. Ce nom de dossier doit correspondre au nom de la classe de composant de vue ou au nom de la classe moins le suffixe. Si l’attribut ViewComponent est utilisé, le nom de la classe doit correspondre à la désignation de l’attribut.

  • Créez une vue RazorViews/Shared/Components/PriorityList/Default.cshtml :

    @model IEnumerable<ViewComponentSample.Models.TodoItem>
    
    <h3>Priority Items</h3>
    <ul>
        @foreach (var todo in Model)
        {
            <li>@todo.Name</li>
        }
    </ul>
    

    La vue Razor prend une liste de tâches TodoItem et les affiche. Si la méthode InvokeAsync de composant de vue ne passe pas le nom de la vue, le nom de vue Default est utilisé par convention. Pour substituer le style par défaut d’un contrôleur spécifique, ajoutez une vue dans le dossier des vues du contrôleur (par exemple, Views/ToDo/Components/PriorityList/Default.cshtml).

    Si le composant de vue est propre au contrôleur, il peut être ajouté au dossier spécifique du contrôleur. Par exemple, Views/ToDo/Components/PriorityList/Default.cshtml est propre au contrôleur.

  • Ajoutez une balise div contenant un appel au composant de liste des priorités au bas du fichier Views/ToDo/index.cshtml :

    </table>
    
    <div>
        Maximum Priority: @ViewData["maxPriority"] <br />
        Is Complete:  @ViewData["isDone"]
        @await Component.InvokeAsync("PriorityList",
                         new { 
                             maxPriority =  ViewData["maxPriority"],
                             isDone = ViewData["isDone"]  }
                         )
    </div>
    

Le balisage @await Component.InvokeAsync montre la syntaxe utilisée pour appeler un composant de vue. Le premier argument est le nom du composant à appeler. Les paramètres suivants sont passés au composant. InvokeAsync peut prendre un nombre arbitraire d’arguments.

Tester l'application. L’image suivante montre la liste des tâches ToDo et les tâches prioritaires :

todo list and priority items

Le composant de vue peut être appelé directement à partir du contrôleur :

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

priority items from IndexVC action

Spécifier un nom de composant de vue

Un composant de vue complexe peut avoir besoin de spécifier une autre vue que la vue par défaut dans certaines conditions. Le code suivant montre comment spécifier la vue « PVC » à partir de la méthode InvokeAsync. Mettez à jour la méthode InvokeAsync dans la classe PriorityListViewComponent.

public async Task<IViewComponentResult> InvokeAsync(
                                           int maxPriority, bool isDone)
{
    string MyView = "Default";
    // If asking for all completed tasks, render with the "PVC" view.
    if (maxPriority > 3 && isDone == true)
    {
        MyView = "PVC";
    }
    var items = await GetItemsAsync(maxPriority, isDone);
    return View(MyView, items);
}

Copiez le fichier Views/Shared/Components/PriorityList/Default.cshtml dans une vue nommée Views/Shared/Components/PriorityList/PVC.cshtml. Ajoutez un titre pour indiquer que la vue PVC est utilisée.

@model IEnumerable<ViewComponentSample.Models.TodoItem>

<h2> PVC Named Priority Component View</h2>
<h4>@ViewBag.PriorityMessage</h4>
<ul>
    @foreach (var todo in Model)
    {
        <li>@todo.Name</li>
    }
</ul>

Exécutez l’application et vérifiez la vue PVC.

Priority View Component

Si la vue PVC ne s’affiche pas, vérifiez que le composant de vue de priorité 4 ou plus est appelé.

Vérifier le chemin de la vue

  • Changez le paramètre de priorité à une priorité inférieure ou égale à 3 pour empêcher l’affichage de la vue des priorités.

  • Renommez temporairement Views/ToDo/Components/PriorityList/Default.cshtml en 1Default.cshtml.

  • Testez l’application, l’erreur suivante se produit :

    An unhandled exception occurred while processing the request.
    InvalidOperationException: The view 'Components/PriorityList/Default' wasn't found. The following locations were searched:
    /Views/ToDo/Components/PriorityList/Default.cshtml
    /Views/Shared/Components/PriorityList/Default.cshtml
    
  • Copiez Views/ToDo/Components/PriorityList/1Default.cshtml dans Views/Shared/Components/PriorityList/Default.cshtml.

  • Ajoutez du balisage à la vue ToDo Shared du composant de vue pour indiquer que la vue provient du dossier Shared.

  • Testez la vue de composant Shared.

ToDo output with Shared component view

Éviter les chaînes codées en dur

Par mesure de sécurité, au moment de la compilation, remplacez le nom du composant de vue codé en dur par le nom de la classe. Mettez à jour le fichier PriorityListViewComponent.cs pour éviter qu’il utilise le suffixe « ViewComponent » :

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityList : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityList(ToDoContext context)
    {
        db = context;
    }

    public async Task<IViewComponentResult> InvokeAsync(
                                               int maxPriority, bool isDone)
    {
        var items = await GetItemsAsync(maxPriority, isDone);
        return View(items);
    }

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

Le fichier de vue :

</table>

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Une surcharge de la méthode Component.InvokeAsync qui prend un type CLR utilise l’opérateur typeof :

</table>

<div>
    Testing typeof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(typeof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Effectuer un travail synchrone

Le framework gère l’appel d’une méthode Invoke synchrone si le travail asynchrone n’est pas nécessaire. La méthode suivante crée un composant de vue Invoke synchrone :

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityListSync : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityListSync(ToDoContext context)
        {
            db = context;
        }

        public IViewComponentResult Invoke(int maxPriority, bool isDone)
        {
 
            var x = db!.ToDo!.Where(x => x.IsDone == isDone &&
                                  x.Priority <= maxPriority).ToList();
            return View(x);
        }
    }
}

Le fichier Razor du composant de vue :

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityListSync),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Le composant de vue est appelé dans un fichier Razor (par exemple, Views/Home/Index.cshtml) en utilisant l’une des approches suivantes :

Pour utiliser l’approche IViewComponentHelper, appelez Component.InvokeAsync :

@await Component.InvokeAsync(nameof(PriorityList),
                             new { maxPriority = 4, isDone = true })

Pour utiliser le Tag Helper, inscrivez l’assembly contenant le composant de vue à l’aide de la directive @addTagHelper (le composant de vue se trouve dans un assembly appelé MyWebApp) :

@addTagHelper *, MyWebApp

Utilisez le Tag Helper du composant de vue dans le fichier de balisage Razor :

<vc:priority-list max-priority="999" is-done="false">
</vc:priority-list>

La signature de méthode de PriorityList.Invoke est synchrone, mais Razor recherche et appelle la méthode avec Component.InvokeAsync dans le fichier de balisage.

Ressources supplémentaires

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Composants de vue

Les composants de vue sont similaires aux vues partielles, mais ils sont beaucoup plus puissants. Les composants de vue n’utilisent pas la liaison de données ; ils dépendent uniquement des données fournies en réponse à l’appel d’un composant de vue. Cet article a été écrit en utilisant des contrôleurs et des vues, mais les composants de vue fonctionnent également avec Razor Pages.

Un composant de vue a les caractéristiques suivantes :

  • Il effectue le rendu d’un bloc de code au lieu d’une réponse entière.
  • Il garantit la même « séparation des préoccupations » et offre les mêmes avantages de testabilité qu’entre un contrôleur et une vue.
  • Il peut avoir des paramètres et une logique métier.
  • Il est généralement appelé à partir d’une page de disposition.

Les composants de vue sont conçus pour être utilisés là où vous avez une logique de rendu réutilisable qui est trop complexe pour une vue partielle. Ils ciblent les vues suivantes, par exemple :

  • Menus de navigation dynamiques
  • Nuage de mots clés (pour l’interrogation de la base de données)
  • Panneau de connexion
  • Panier d’achat
  • Articles récemment publiés
  • Contenu de la barre latérale dans un blog standard
  • Panneau de connexion inclus dans chaque page pour afficher les liens de connexion ou déconnexion, en fonction de l’état de connexion de l’utilisateur

Un composant de vue est constitué de deux parties : la classe (généralement dérivée de ViewComponent) et le résultat qu’il retourne (en général, une vue). Comme les contrôleurs, un composant de vue peut être un OCT, mais la plupart des développeurs tirent parti des méthodes et des propriétés dérivées de ViewComponent.

Lorsque vous vérifiez si des composants de vue répondent aux spécifications d’une application, utilisez plutôt des composants Razor. Les composants Razor combinent également du balisage avec le code C# pour produire des unités d’interface utilisateur réutilisables. Les composants Razor ont été pensés pour favoriser la productivité des développeurs lorsqu’ils fournissent une logique et une composition d’interface utilisateur côté client. Pour plus d’informations, consultez Composants ASP.NET Core Razor. Pour savoir comment incorporer des composants Razor dans une application MVC ou Razor Pages, consultez Effectuer le prérendu et l’intégration de composants Razor ASP.NET Core.

Création d’un composant de vue

Cette section présente les exigences générales relatives à la création d’un composant de vue. Plus loin dans cet article, nous décrirons chaque étape en détail et nous créerons un composant de vue.

Classe de composant de vue

Vous pouvez créer une classe de composant de vue à l’aide d’une des méthodes suivantes :

  • En la dérivant de ViewComponent
  • En décorant une classe avec l’attribut [ViewComponent] ou en dérivant la classe d’une classe définie avec l’attribut [ViewComponent]
  • En créant une classe dont le nom se termine par le suffixe ViewComponent

Comme les contrôleurs, les composants de vue doivent être des classes publiques, non imbriquées et non abstraites. Le nom du composant de vue correspond au nom de la classe sans le suffixe ViewComponent. Il peut également être spécifié explicitement à l’aide de la propriété ViewComponentAttribute.Name.

Une classe de composant de vue :

  • Prend entièrement en charge l’injection de dépendances dans le constructeur
  • N’intervient pas dans le cycle de vie du contrôleur, ce qui signifie que vous ne pouvez pas utiliser de filtres dans un composant de vue

Pour éviter qu’une classe qui possède un suffixe ViewComponent insensible à la casse soit traité en tant que composant de vue, décorez la classe avec l’attribut [NonViewComponent] :

[NonViewComponent]
public class ReviewComponent
{
    // ...

Méthodes d’un composant de vue

Un composant de vue définit sa logique dans une méthode InvokeAsync qui retourne un Task<IViewComponentResult>, ou dans une méthode Invoke synchrone qui retourne un IViewComponentResult. Les paramètres sont fournis directement en réponse à l’appel du composant de vue ; ils ne proviennent pas de la liaison de données. Un composant de vue ne traite jamais une requête directement. En règle générale, un composant de vue initialise un modèle et le passe à une vue en appelant la méthode View. En résumé, les méthodes d’un composant de vue :

  • Définissent une InvokeAsync méthode qui retourne un Task<IViewComponentResult> ou une méthode Invoke synchrone qui retourne un IViewComponentResult.
  • Permettent généralement d’initialiser un modèle et de le passer à une vue en appelant la méthode View de ViewComponent.
  • Les paramètres proviennent de la méthode appelante, et non pas de HTTP. Il n’y a pas de liaison de modèle.
  • Ne sont pas accessibles directement comme point de terminaison HTTP. Elles sont appelées depuis votre code (généralement dans une vue). Un composant de vue ne traite jamais une requête.
  • Sont surchargées sur la signature, plutôt que sur des détails de la requête HTTP en cours.

Chemin de recherche de la vue

Le Runtime recherche la vue dans les chemins suivants :

  • /Views/{Controller Name}/Components/{View Component Name}/{View Name}
  • /Views/Shared/Components/{View Component Name}/{View Name}
  • /Pages/Shared/Components/{View Component Name}/{View Name}
  • /Areas/{Area Name}/Views/Shared/Components/{View Component Name}/{View Name}

Le chemin de recherche s’applique aux projets utilisant contrôleurs + vues et Razor Pages.

Le nom de la vue par défaut pour un composant de vue est Default, ce qui signifie que votre fichier de vue se nomme normalement Default.cshtml. Vous pouvez spécifier un nom de vue différent quand vous créez le résultat du composant de vue ou quand vous appelez la méthode View.

Nous vous recommandons de nommer le fichier de vue Default.cshtmlet d’utiliser le chemin Views/Shared/Components/{View Component Name}/{View Name}. Le composant de vue PriorityList utilisé dans cet exemple utilise Views/Shared/Components/PriorityList/Default.cshtml pour la vue.

Personnaliser le chemin de recherche de vues

Pour personnaliser le chemin de recherche de vues, modifiez la collection ViewLocationFormats de Razor. Par exemple, pour rechercher des vues dans le chemin « /Components/{View Component Name}/{View Name} », ajoutez un nouvel élément à la collection :

services.AddMvc()
    .AddRazorOptions(options =>
    {
        options.ViewLocationFormats.Add("/{0}.cshtml");
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

Dans le code précédent, l’espace réservé « {0} » représente le chemin « Components/{View Component Name}/{View Name} ».

Appel d’un composant de vue

Pour utiliser le composant de vue, appelez-le dans une vue, comme ci-dessous :

@await Component.InvokeAsync("Name of view component", {Anonymous Type Containing Parameters})

Les paramètres sont passés à la méthode InvokeAsync. Le composant de vue PriorityList développé dans cet article est appelé à partir du fichier de vue Views/ToDo/Index.cshtml. Dans le code suivant, la méthode InvokeAsync est appelée avec deux paramètres :

@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })

Appel d’un composant de vue en tant que Tag Helper

Dans ASP.NET Core 1.1 et les versions ultérieures, vous pouvez appeler un composant de vue en tant que Tag Helper :

<vc:priority-list max-priority="2" is-done="false">
</vc:priority-list>

Les paramètres de méthode et de classe de casse Pascal pour les Tag Helpers sont convertis en casse kebab (mots séparés par des tirets). Le Tag Helper utilisé pour appeler un composant de vue contient l’élément <vc></vc>. Le composant de vue est spécifié de la façon suivante :

<vc:[view-component-name]
  parameter1="parameter1 value"
  parameter2="parameter2 value">
</vc:[view-component-name]>

Pour utiliser un composant de vue en tant que Tag Helper, inscrivez l’assembly contenant le composant de vue à l’aide de la directive @addTagHelper. Si votre composant de vue se trouve dans un assembly appelé MyWebApp, ajoutez la directive suivante au fichier _ViewImports.cshtml :

@addTagHelper *, MyWebApp

Vous pouvez inscrire un composant de vue en tant que Tag Helper dans n’importe quel fichier qui référence ce composant. Pour plus d’informations sur l’inscription des Tag Helpers, consultez Gestion de l’étendue des Tag Helpers.

Méthode InvokeAsync utilisée dans ce didacticiel :

@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })

Dans le balisage Tag Helper :

<vc:priority-list max-priority="2" is-done="false">
</vc:priority-list>

Dans l’exemple ci-dessus, le composant de vue PriorityList devient priority-list. Les paramètres sont passés au composant de vue sous forme d’attributs dans la casse kebab.

Appel d’un composant de vue directement à partir d’un contrôleur

Les composants de vue sont généralement appelés depuis une vue, mais ils peuvent aussi être appelés directement d’une méthode de contrôleur. Les composants de vue ne définissent pas de points de terminaison comme le font les contrôleurs, mais vous pouvez facilement implémenter une action de contrôleur qui retourne le contenu d’un ViewComponentResult.

Dans cet exemple, le composant de vue est appelé directement du contrôleur :

public IActionResult IndexVC()
{
    return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}

Procédure pas à pas : Création d’un composant de vue simple

Téléchargez, générez et testez le code de démarrage. Il s’agit d’un projet simple avec un contrôleur ToDo qui affiche une liste de tâches ToDo.

List of ToDos

Ajouter une classe ViewComponent

Créez un dossier ViewComponents et ajoutez la classe PriorityListViewComponent suivante :

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityListViewComponent : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityListViewComponent(ToDoContext context)
        {
            db = context;
        }

        public async Task<IViewComponentResult> InvokeAsync(
        int maxPriority, bool isDone)
        {
            var items = await GetItemsAsync(maxPriority, isDone);
            return View(items);
        }
        private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
        {
            return db.ToDo.Where(x => x.IsDone == isDone &&
                                 x.Priority <= maxPriority).ToListAsync();
        }
    }
}

Remarques sur le code :

  • Les classes ViewComponent peuvent être ajoutées à n’importe quel dossier dans le projet.

  • Étant donné que le nom de classe PriorityListViewComponent se termine par le suffixe ViewComponent, le runtime utilise la chaîne PriorityList pour référencer le composant de classe à partir d’une vue.

  • L’attribut [ViewComponent] peut changer le nom utilisé pour faire référence à un composant de vue. Par exemple, la classe peut avoir été nommée XYZ avec l’attribut ViewComponent :

    [ViewComponent(Name = "PriorityList")]
       public class XYZ : ViewComponent
    
  • L’attribut [ViewComponent] présent dans le code précédent indique au sélecteur de composant de vue d’utiliser :

    • Le nom PriorityList lors de la recherche des vues associées au composant
    • La chaîne « PriorityList » lors du référencement du composant de classe à partir d’une vue.
  • Le composant utilise l’injection de dépendances pour rendre le contexte de données disponible.

  • InvokeAsync expose une méthode qui peut être appelée à partir d’une vue et qui peut prendre un nombre arbitraire d’arguments.

  • La méthode InvokeAsync retourne l’ensemble des tâches ToDo qui correspondent aux paramètres isDone et maxPriority spécifiés.

Créer la vue Razor du composant de vue

  • Créez le dossier Views/Shared/Components. Ce dossier doit être nommé Components.

  • Créez le dossier Views/Shared/Components/PriorityList. Ce nom de dossier doit correspondre au nom de la classe du composant de vue, ou au nom de la classe sans le suffixe (dans le cas où vous avez suivi la convention et utilisé le suffixe ViewComponent dans le nom de la classe). Si vous avez utilisé l’attribut ViewComponent, le nom de la classe doit correspondre à la désignation de l’attribut.

  • Créez une vue RazorViews/Shared/Components/PriorityList/Default.cshtml :

    @model IEnumerable<ViewComponentSample.Models.TodoItem>
    
    <h3>Priority Items</h3>
    <ul>
        @foreach (var todo in Model)
        {
            <li>@todo.Name</li>
        }
    </ul>
    

    La vue Razor prend une liste de tâches TodoItem et les affiche. Si la méthode InvokeAsync du composant de vue ne passe pas le nom de la vue (comme dans notre exemple), le nom de vue Default est utilisé par convention. Je vous montrerai comment passer le nom de la vue plus tard dans ce didacticiel. Pour substituer le style par défaut d’un contrôleur spécifique, ajoutez une vue dans le dossier des vues du contrôleur (par exemple, Views/ToDo/Components/PriorityList/Default.cshtml).

    Si le composant de vue est propre au contrôleur, il peut être ajouté au dossier spécifique du contrôleur (Views/ToDo/Components/PriorityList/Default.cshtml).

  • Ajoutez une balise div contenant un appel au composant de liste des priorités au bas du fichier Views/ToDo/index.cshtml :

    </table>
    <div>
        @await Component.InvokeAsync("PriorityList", new { maxPriority = 2, isDone = false })
    </div>
    

Le balisage @await Component.InvokeAsync montre la syntaxe utilisée pour appeler un composant de vue. Le premier argument est le nom du composant à appeler. Les paramètres suivants sont passés au composant. InvokeAsync peut prendre un nombre arbitraire d’arguments.

Tester l'application. L’image suivante montre la liste des tâches ToDo et les tâches prioritaires :

todo list and priority items

Vous pouvez également appeler le composant de vue directement à partir du contrôleur :

public IActionResult IndexVC()
{
    return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}

priority items from IndexVC action

Spécification d’un nom de vue

Un composant de vue complexe peut avoir besoin de spécifier une autre vue que la vue par défaut dans certaines conditions. Le code suivant montre comment spécifier la vue « PVC » à partir de la méthode InvokeAsync. Mettez à jour la méthode InvokeAsync dans la classe PriorityListViewComponent.

public async Task<IViewComponentResult> InvokeAsync(
    int maxPriority, bool isDone)
{
    string MyView = "Default";
    // If asking for all completed tasks, render with the "PVC" view.
    if (maxPriority > 3 && isDone == true)
    {
        MyView = "PVC";
    }
    var items = await GetItemsAsync(maxPriority, isDone);
    return View(MyView, items);
}

Copiez le fichier Views/Shared/Components/PriorityList/Default.cshtml dans une vue nommée Views/Shared/Components/PriorityList/PVC.cshtml. Ajoutez un titre pour indiquer que la vue PVC est utilisée.

@model IEnumerable<ViewComponentSample.Models.TodoItem>

<h2> PVC Named Priority Component View</h2>
<h4>@ViewBag.PriorityMessage</h4>
<ul>
    @foreach (var todo in Model)
    {
        <li>@todo.Name</li>
    }
</ul>

Mettez à jour Views/ToDo/Index.cshtml :

@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })

Exécutez l’application et vérifiez la vue PVC.

Priority View Component

Si la vue PVC ne s’affiche pas, vérifiez que vous appelez le composant de vue avec une priorité supérieure ou égale à 4.

Vérifier le chemin de la vue

  • Changez le paramètre de priorité à une priorité inférieure ou égale à 3 pour empêcher l’affichage de la vue des priorités.

  • Renommez temporairement Views/ToDo/Components/PriorityList/Default.cshtml en 1Default.cshtml.

  • Testez l’application. Vous obtenez l’erreur suivante :

    An unhandled exception occurred while processing the request.
    InvalidOperationException: The view 'Components/PriorityList/Default' wasn't found. The following locations were searched:
    /Views/ToDo/Components/PriorityList/Default.cshtml
    /Views/Shared/Components/PriorityList/Default.cshtml
    EnsureSuccessful
    
  • Copiez Views/ToDo/Components/PriorityList/1Default.cshtml dans Views/Shared/Components/PriorityList/Default.cshtml.

  • Ajoutez du balisage à la vue ToDo Shared du composant de vue pour indiquer que la vue provient du dossier Shared.

  • Testez la vue de composant Shared.

ToDo output with Shared component view

Éviter les chaînes codées en dur

Pour éviter d’éventuels problèmes au moment de la compilation, vous pouvez remplacer le nom du composant de vue codé en dur par le nom de la classe. Créez le composant de vue sans le suffixe « ViewComponent » :

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityList : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityList(ToDoContext context)
        {
            db = context;
        }

        public async Task<IViewComponentResult> InvokeAsync(
        int maxPriority, bool isDone)
        {
            var items = await GetItemsAsync(maxPriority, isDone);
            return View(items);
        }
        private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
        {
            return db.ToDo.Where(x => x.IsDone == isDone &&
                                 x.Priority <= maxPriority).ToListAsync();
        }
    }
}

Ajoutez une instruction using à votre fichier de vue Razor, puis utilisez l’opérateur nameof :

@using ViewComponentSample.Models
@using ViewComponentSample.ViewComponents
@model IEnumerable<TodoItem>

    <h2>ToDo nameof</h2>
    <!-- Markup removed for brevity.  -->

    <div>

        @*
            Note: 
            To use the below line, you need to #define no_suffix in ViewComponents/PriorityList.cs or it won't compile.
            By doing so it will cause a problem to index as there will be multiple viewcomponents 
            with the same name after the compiler removes the suffix "ViewComponent"
        *@

        @*@await Component.InvokeAsync(nameof(PriorityList), new { maxPriority = 4, isDone = true })*@
    </div>

Vous pouvez utiliser une surcharge de la Component.InvokeAsync méthode qui prend un type CLR. N’oubliez pas d’utiliser l’opérateur typeof dans ce cas :

@using ViewComponentSample.Models
@using ViewComponentSample.ViewComponents
@model IEnumerable<TodoItem>

<h2>ToDo typeof</h2>
<!-- Markup removed for brevity.  -->

<div>
    @await Component.InvokeAsync(typeof(PriorityListViewComponent), new { maxPriority = 4, isDone = true })
</div>

Effectuer un travail synchrone

Le framework gère l’appel d’une méthode Invoke synchrone si vous n’avez pas besoin d’effectuer un travail asynchrone. La méthode suivante crée un composant de vue Invoke synchrone :

public class PriorityList : ViewComponent
{
    public IViewComponentResult Invoke(int maxPriority, bool isDone)
    {
        var items = new List<string> { $"maxPriority: {maxPriority}", $"isDone: {isDone}" };
        return View(items);
    }
}

Le fichier Razor du composant de vue liste les chaînes transmises à la méthode Invoke (Views/Home/Components/PriorityList/Default.cshtml) :

@model List<string>

<h3>Priority Items</h3>
<ul>
    @foreach (var item in Model)
    {
        <li>@item</li>
    }
</ul>

Le composant de vue est appelé dans un fichier Razor (par exemple, Views/Home/Index.cshtml) en utilisant l’une des approches suivantes :

Pour utiliser l’approche IViewComponentHelper, appelez Component.InvokeAsync :

@await Component.InvokeAsync(nameof(PriorityList), new { maxPriority = 4, isDone = true })

Pour utiliser le Tag Helper, inscrivez l’assembly contenant le composant de vue à l’aide de la directive @addTagHelper (le composant de vue se trouve dans un assembly appelé MyWebApp) :

@addTagHelper *, MyWebApp

Utilisez le Tag Helper du composant de vue dans le fichier de balisage Razor :

<vc:priority-list max-priority="999" is-done="false">
</vc:priority-list>

La signature de méthode de PriorityList.Invoke est synchrone, mais Razor recherche et appelle la méthode avec Component.InvokeAsync dans le fichier de balisage.

Tous les paramètres du composant de vue sont requis.

Chaque paramètre d’un composant de vue est un attribut requis. Consultez ce problème GitHub. Si un paramètre est omis :

  • La signature de la méthode InvokeAsync ne correspond pas, et la méthode n’est pas exécutée.
  • ViewComponent n’affiche aucun balisage.
  • Aucune erreur n’est levée.

Ressources supplémentaires