ASP.NET Core View Components
ViewComponent
klasse gebruiken.Inleiding
View componenten zijn nieuw in ASP.NET Core en zijn vergelijkbaar met partial views. Deze componenten zijn krachtiger dan partial views. Ze gebruiken echter geen model binding. Ze zijn enkel en alleen afhankelijk van de gegevens die je meegeeft wanneer je ze oproept:
- ze renderen een gedeelte van de pagina in plaats van een hele pagina als antwoord;
- volgen hetzelfde SEC (separation of concern) patroon als controllers;
- zijn even testbaarheid als controllers;
- kunnen parameters en business logica bevatten;
- worden meestal aangeroepen vanuit een lay-pagina;
Componenten zijn goede kandidaten bij refactoring, wanneer je vaststelt dat de herbruikbare rendering logica te complex is om in partial views te stoppen. Denk bijvoorbeeld aan:
- dynamische navigatie menu's;
- inlog paneel;
- winkelmand;
- recent verschenen artikels;
- lijst van artikels (bijvoorbeeld lijst van landen, basiseenheden, klanten, producten enz.);
- sidebar content op een typische blog;
- een login panel dat op elke pagina weergegeven wordt en links toont om af te melden of aan te melden afhankelijk van de login toestand van de gebruiker;
Een view component bestaat uit twee delen:
- de klasse (gewoonlijk afgeleid van
ViewComponent
); - het resultaat dat geretourneerd wordt, meestal een view;
Een view component maken
Een view component klasse kan je op drie verschillende manieren maken:
- maak een klasse die overerft van ViewComponent;
- decoreer een klase met het [ViewComponent] attribuut of een klasse afleiden van een klasse met een [ViewComponent] attribuut;
- een klasse maken waarvan de naam eindigt met het achtervoegsel ViewComponent;
De derde methode gaan wij in ons voorbeeld toepassen.
De methoden van een view component
De logica van een view component wordt gedefinieerd in een InvokeAsync
methode die een IViewComponentResult
retourneert. Parameters worden meegegeven wanneer de view component wordt aangeroepen, niet vanuit model binding. Een view component handelt nooit rechtstreeks een request af.
Een view component initialiseert zelf een model en geeft het door aan een view door de View-methode op te roepen.
Samengevat:
definieer een InvokeAsync
methode die een IViewComponentResult
retourneert;
initialiseert een model en geef het door aan een view door een ViewComponent View
methode op te roepen;
parameters worden ingevuld door de argumenten die door de aanroepende methode worden meegegeven en niet door HTTP, er is geen model binding;
view components zijn niet direct te bereiken als HTTP-eindpunt, ze worden rechtstreels aangeroepen vanuit je code (meestal in de view). Een view component handelt nooit een request af;
View zoekpad
De runtime zoekt de view langs ee, de volgende twee wegen:
Views/<controller_name>/Components/<view_component_name>/<VIEW_NAME>
- Views/Shared/Components/<view_component_name>/<VIEW_NAME>
De standaard view naam voor een component is Default
, wat betekent dat een view bestand doorgaans de naam Default.cshtml zal dragen. Je kan een andere naam aan de view geven maar dan moet je die naam ook expliceit opgeven als je de view-methode aanroept.
.NET Core raadt aan het view bestand de naam Default.cshtml te geven en deze te plaatsen in de map met de naam Views/Shared/Components/<view_component_name>/<VIEW_NAME>.
In het voorbeeld hieronder heet de view Views/Shared/Components/CountryAll/Default.cshtml.
Een view component aanroepen
Om een view component aan te roepen gebruik je
@Component.InvokeAsync ("Naam van view component", <anonieme gegeventypes die parameters bevatten>)
vanuit uit een view.
De parameters worden doorgegeven aan de InvokeAsync
methode. De ReadAll view component die we hieronder maken wordt opgeroepen vanuit de Views/ Country/Create.cshtml view bestand:
@await Component.InvokeAsync("CountryAll")
Een view component aanroepen vanuit de controller
Viewcomponenten worden meestal aangeroepen uit een view. Maar refactoring zou niet volledig zijn als je zo ook niet rechtstreeks vanuit een controller methode zou kunnen aanroepen. Hoewel view componenten geen HTTP-eindpunten zijn zoals controllers, kan je gemakkelijk een controller actie schrijven die de inhoud van een ViewComponentResult
retourneert:
Je kan de volgende methode in de CountryController
toevoegen:
public IActionResult ReadAll () { returnViewComponent ("CountryAll"); }
Stappenplan implementeren CoutryAllViewComponent
Een ViewComponent toevoegen
Maak de CountryAllViewComponent
klasse in ViewComponents/CountryAllViewComponent.cs:
using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Mikmak.Models; using System.Collections; namespace Mikmak.ViewComponents { public class CountryAllViewComponent : ViewComponent { private readonly MikmakDbContext _context; public CountryAllViewComponent(MikmakDbContext context) { _context = context; } public async Task<IViewComponentResult> InvokeAsync() { IEnumerable list = await _context.Country.ToListAsync(); return View(list); } } }
De view
Maak de Razor view voor de component in het bestand Views\Shared\Components\CountryAll\Default.cshtml:
@model IEnumerable<Mikmak.Models.Country> <table class="list"> <thead> <tr> <th> @Html.DisplayNameFor(model => model.Code) </th> <th> @Html.DisplayNameFor(model => model.Name) </th> <th></th> </tr> </thead> <tbody> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Code) </td> <td> @Html.DisplayFor(modelItem => item.Name) </td> <td> <a asp-action="Details" asp-route-id="@item.Id">Details</a> <a href="/Country/Details/@item.Id">Details</a> </td> </tr> } </tbody> </table>
De component aanroepen
De component roep je aan in de Razor view met de naam Views\Country\Create.cshtml:
@model Mikmak.Models.Country
@{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<form method="post" action="/Country/Create">
<div class="form-horizontal">
<h4>Country</h4>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Code" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Code" class="form-control" />
<input name="Code" id="Code" type="text" class="form-control" />
<span asp-validation-for="Code" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Latitude" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Latitude" class="form-control" />
<span asp-validation-for="Latitude" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Longitude" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Longitude" class="form-control" />
<span asp-validation-for="Longitude" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Name" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="ShippingCostMultiplier" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="ShippingCostMultiplier" class="form-control" />
<span asp-validation-for="ShippingCostMultiplier" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
@await Component.InvokeAsync("CountryAll")
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}