Cómo crear ASP.NET8 View-Components, que son una forma más poderosa de arquitectura de componentes que las populares Partial-Views.
Cómo crear ASP.NET8 View-Components, que son una forma más poderosa de arquitectura de componentes que las populares Partial-Views.Los componentes de visualización de ASP.NET8 son más potentes que los componentes de visualización parcial
En pocas palabras, los componentes de visualización de ASP.NET8 son similares a los de visualización parcial, pero permiten un mayor nivel de componentización y encapsulación de la funcionalidad.
Los componentes de visualización de ASP.NET8 están diseñados para la creación de componentes reutilizables que tengan la funcionalidad de visualización / renderización, y cuya complejidad es mayor que las vistas parciales normales.
Principales ventajas sobrepor ASP.NETVista parcial es que los componentes de vista tienen unaparte de componente que se ejecuta en el lado del servidory puede acceder a todos los mecanismos de la arquitectura de aplicaciones, como la capa de acceso a la base de datos (por ejemplo, EF) independientemente de la página de alojamiento donde se alojan.
parte de componente que se ejecuta en el lado del servidorASP.NET8 View-Components recibe parámetros del método de llamada (página de alojamiento o incluso controlador directamente) y no la solicitud HTTP. Pero, por supuesto, nada impide la creación de View-Components que reciban HttpRequest como un parámetro que será explicitamente transmitido al componente.
Ver componentes enpor ASP.NETLos núcleos están bien definidos en el artículo [1], y no lo repetiré todo aquí. El plan es proporcionar el código de muestra de C# (“Primer”) sobre cómo se pueden utilizar los componentes de visualización típicamente. código reutilizable con un ejemplo simple se muestra a continuación. he añadido una cantidad decente de comentarios, por lo que debe ser autoexplicativo cómo funcionan.
2 El resultado final
Aquí mostramos el resultado final. Hay ASP.NET8 página de alojamiento tiene 2 componentes y 4 invocaciones que se muestran en la imagen. Los componentes son coloreados deliberadamente para mostrar dónde están. En una aplicación real, por supuesto, el colorado no se usaría. Aquí está lo que este ejemplo muestra:
-
Div-0. Control in Host ASP.NET8 page
-
Div-1. View-Component-1 CustomerSearch, which is Async component, invoked with a method
-
Div-2. View-Component-1 CustomerSearch, which is Async component, invoked with Tag Helper
-
Div-3. View-Component-2 CustomerSearch2, which is Sync component, invoked with a method
-
Div-4. View-Component-2 CustomerSearch2, which is Sync component, invoked with Tag Helper
El código fuente para este ejemplo
El código fuente es bien comentado, y debe ser autoexplicativo.
3.1 Parte C#
//HomeController.cs=========================================
namespace Example1.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
//in this action we are testing the ViewComponent
public IActionResult Test1(Test1_ViewModel model)
{
return View(model);
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
// CustomersSearchViewComponent_Model.cs ==========================
namespace Example1.ViewComponents.Models
{
public class CustomersSearchViewComponent_Model
{
public string? Parameter1String { get; set; } = null;
public int? Parameter2Int { get; set; } = null;
public string? IdOfTargetInputField { get; set; } = null;
}
}
// CustomersSearchViewComponent_ViewModel.cs ==========================
namespace Example1.ViewComponents.Models
{
public class CustomersSearchViewComponent_ViewModel
{
public string? Parameter1String { get; set; } = null;
public int? Parameter2Int { get; set; } = null;
public string? IdOfTargetInputField { get; set; } = null;
//this should come from DB in real application
public SelectList? ListOfCustomers { get; set; }
}
}
//CustomersSearch2ViewComponent.cs===================================================
namespace Example1.ViewComponents
{
//it should inherit from ViewComponent class
//this is Sync version of ViewComponent
public class CustomersSearch2ViewComponent : ViewComponent
{
private readonly ILogger<CustomersSearch2ViewComponent>? _logger;
// We are testing Dependency Injection (DI) to inject the logger
public CustomersSearch2ViewComponent(ILogger<CustomersSearch2ViewComponent> logger)
{
//note how useful is this, we are passing in this constructor
//into view-component objects that are part of app DI container
//you can pass here any form of reference to DataBase, for example
//like EF DbContext or similar
_logger = logger;
}
//The Invoke method for the View component
public IViewComponentResult Invoke(
CustomersSearchViewComponent_Model? model = null
)
{
//this is neat, this parameters in CustomersSearchViewComponent_Model
//are passed to the view-component from host form
{
//testing that DI worked - logger
string methodName = $"Type: {System.Reflection.MethodBase.GetCurrentMethod()?.DeclaringType?.FullName}, " +
$"Method: InvokeAsync; ";
_logger?.LogWarning(methodName);
}
//preparing view-model
CustomersSearchViewComponent_ViewModel viewModel = new();
viewModel.Parameter1String = model?.Parameter1String;
viewModel.Parameter2Int = model?.Parameter2Int;
viewModel.IdOfTargetInputField = model?.IdOfTargetInputField;
{
//this should come from DB in real application
SelectList list1 = new SelectList
(new List<SelectListItem>
{
new SelectListItem
{
Text = "John - 111",
Value = "111"
},
new SelectListItem
{
Text = "Mark - 222",
Value = "222"
},
new SelectListItem
{
Text = "Novak - 333",
Value = "333"
},
}, "Value", "Text");
viewModel.ListOfCustomers = list1;
}
//now render component view
return View("Default", viewModel);
}
}
}
//CustomersSearchViewComponent.cs===================================================
namespace Example1.ViewComponents
{
//it should inherit from ViewComponent class
//this is Async version of ViewComponent
public class CustomersSearchViewComponent : ViewComponent
{
private readonly ILogger<CustomersSearchViewComponent>? _logger;
// We are testing Dependency Injection (DI) to inject the logger
public CustomersSearchViewComponent(ILogger<CustomersSearchViewComponent> logger)
{
//note how useful is this, we are passing in this constructor
//into view-component objects that are part of app DI container
//you can pass here any form of reference to DataBase, for example
//like EF DbContext or similar
_logger = logger;
}
//The Invoke method for the View component
public async Task<IViewComponentResult> InvokeAsync(
CustomersSearchViewComponent_Model? model=null
)
{
//this is neat, this parameters in CustomersSearchViewComponent_Model
//are passed to the view-component from host form
{
//testing that DI worked - logger
string methodName = $"Type: {System.Reflection.MethodBase.GetCurrentMethod()?.DeclaringType?.FullName}, " +
$"Method: InvokeAsync; ";
_logger?.LogWarning(methodName);
}
await Task.Delay(0); // Simulate some async work
//preparing view-model
CustomersSearchViewComponent_ViewModel viewModel = new();
viewModel.Parameter1String = model?.Parameter1String;
viewModel.Parameter2Int = model?.Parameter2Int;
viewModel.IdOfTargetInputField = model?.IdOfTargetInputField;
{
//this should come from DB in real application
SelectList list1 = new SelectList
(new List<SelectListItem>
{
new SelectListItem
{
Text = "John - 111",
Value = "111"
},
new SelectListItem
{
Text = "Mark - 222",
Value = "222"
},
new SelectListItem
{
Text = "Novak - 333",
Value = "333"
},
}, "Value", "Text");
viewModel.ListOfCustomers= list1;
}
//now render component view
return View("Default",viewModel);
}
}
}
3.2 Parte del Razor (.cshtml)
<!--Test1.cshtml --------------------------------------------------->
@using Example1.Models.Home;
@using Example1.ViewComponents;
@using Example1.ViewComponents.Models;
@using Example1
@using Example1.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Example1
@addTagHelper *, Example1.TagHelpers
@model Test1_ViewModel
@{
ViewData["Title"] = "Test1";
<!--Title --------------------------------------------------->
<h5 class="bg-primary text-left p-1 mt-1">
<span class="d-inline-block ms-2">
@ViewData["Title"]
</span>
</h5>
<!-- Flexbox row1 -->
<div class="d-flex" style="width:1100px">
<!-- Div 0-------------------------------------------------->
<div class="m-3 p-3">
<fieldset class="border rounded-3 p-3 bg-light shadow" style="width:500px">
<legend class="float-none w-auto px-3 border bg-light rounded-3 ">
Div - 0
</legend>
<!-- Form ----------------------------------------------------------------- -->
<form id="form1" method="post" class="row">
<div class="form-group">
<label asp-for="ContractOwnerCustomer">
Customer Id
</label>
<input id="ContractOwnerCustomerId" class="form-control" asp-for="ContractOwnerCustomer" />
</div>
</form>
<hr />
<!--Buttons ----------------------------------------------------- -->
<div>
<button type="submit" form="form1" class="btn btn-primary mt-3 me-2 float-end"
href=''>
Submit
</button>
</div>
</fieldset>
</div>
</div>
<!-- Flexbox row2 -->
<div class="d-flex" style="width:1100px">
<!-- Div 1----------------------------------------------- -->
<div class="m-3 p-3">
<h5 class="bg-secondary text-center p-1">
Div1 - CustomersSearch, Async, invoked with method
</h5>
@{
CustomersSearchViewComponent_Model myModel1 = new CustomersSearchViewComponent_Model();
myModel1.Parameter1String = "Div1-Async, invoked with method";
myModel1.Parameter2Int = 11111;
myModel1.IdOfTargetInputField = "ContractOwnerCustomerId";
//so, here we are using the ViewComponent
@await Component.InvokeAsync("CustomersSearch", new
{
model = myModel1,
}) ;
}
</div>
<!-- Div 2----------------------------------------------- -->
<div class="m-3 p-3">
<h5 class="bg-secondary text-center p-1">
Div2 - CustomersSearch, Async, invoked with Tag Helper
</h5>
@{
CustomersSearchViewComponent_Model myModel2 = new CustomersSearchViewComponent_Model();
myModel2.Parameter1String = "Div2-Async, invoked with Tag Helper";
myModel2.Parameter2Int = 22222;
myModel2.IdOfTargetInputField = "ContractOwnerCustomerId";
}
<vc:customers-search model=myModel2>
</vc:customers-search>
</div>
</div>
<!-- Flexbox row3-->
<div class="d-flex" style="width:1100px">
<!-- Div 3----------------------------------------------- -->
<div class="m-3 p-3">
<h5 class="bg-secondary text-center p-1">
Div3 - CustomersSearch2, Sync, invoked with method
</h5>
@{
CustomersSearchViewComponent_Model myModel3 = new CustomersSearchViewComponent_Model();
myModel1.Parameter1String = "Div3-Sync, invoked with method";
myModel1.Parameter2Int = 33333;
myModel1.IdOfTargetInputField = "ContractOwnerCustomerId";
//so, here we are using the ViewComponent
//it looks strange, Sync component invoked with async method
//but they want it this way
@await Component.InvokeAsync("CustomersSearch2", new
{
model = myModel1,
}) ;
}
</div>
<!-- Div 4----------------------------------------------- -->
<div class="m-3 p-3">
<h5 class="bg-secondary text-center p-1">
Div4 - CustomersSearch2, Sync, invoked with Tag Helper
</h5>
@{
CustomersSearchViewComponent_Model myModel4 = new CustomersSearchViewComponent_Model();
myModel2.Parameter1String = "Div4- Sync, invoked with Tag Helper";
myModel2.Parameter2Int = 44444;
myModel2.IdOfTargetInputField = "ContractOwnerCustomerId";
}
<vc:customers-search2 model=myModel2>
</vc:customers-search2>
</div>
<!-- ----------------------------------------------- -->
</div>
}
<!--CustomersSearch\Default.cshtml -------------------------------->
@using Example1.ViewComponents.Models
@model CustomersSearchViewComponent_ViewModel
<!--Visually, this div (color bg-info) is the component--->
<div id="ViewComponents1" class="bg-info p-3" style="width:400px">
<!--Title --------------------------------------------------->
<h5 class="bg-primary text-center p-1 m-3">
CustomersSearch - Async ViewComponent
</h5>
<p>
Proof-of-concept, parameter from Host Form
<br/> Parameter1String: @Model.Parameter1String
</p>
<p>
Proof-of-concept, parameter from Host Form
<br /> Parameter2Int: @Model.Parameter2Int
</p>
<fieldset class="border rounded-3 p-3 m-2 shadow">
<label>
Proof-of-concept, list of customers from DB
</label>
<select id="customerSelect2" class="form-select"
asp-items="@Model.ListOfCustomers">
</select>
<a class="btn btn-secondary mt-3 float-end"
onclick="copyInputTextTo(this)">
Copy Selected Customer ID to Form
</a>
</fieldset>
</div> <!-- End of <div id="ViewComponents1" -->
<script>
// This function will export selection value from the ViewComponent
// to the main form and copy it to the input field
function copyInputTextTo(anchor) {
var root = anchor.closest('fieldset');
var target = document.getElementById("@Model.IdOfTargetInputField");
var source = root.querySelector('#customerSelect2') ;
if (source && target) {
target.value = source.value;
}
return false;
}
</script>
<!--CustomersSearch2\Default.cshtml -------------------------------->
@using Example1.ViewComponents.Models
@model CustomersSearchViewComponent_ViewModel
<!--Visually, this div (color bg-warning) is the component--->
<div id="ViewComponents1" class="bg-warning p-3" style="width:400px">
<!--Title --------------------------------------------------->
<h5 class="bg-primary text-center p-1 m-3">
CustomersSearch2 - Sync ViewComponent
</h5>
<p>
Proof-of-concept, parameter from Host Form
<br /> Parameter1String: @Model.Parameter1String
</p>
<p>
Proof-of-concept, parameter from Host Form
<br /> Parameter2Int: @Model.Parameter2Int
</p>
<fieldset class="border rounded-3 p-3 m-2 shadow">
<label>
Proof-of-concept, list of customers from DB
</label>
<select id="customerSelect2" class="form-select"
asp-items="@Model.ListOfCustomers">
</select>
<a class="btn btn-secondary mt-3 float-end"
onclick="copyInputTextTo(this)">
Copy Selected Customer ID to Form
</a>
</fieldset>
</div> <!-- End of <div id="ViewComponents1" -->
<script>
// This function will export selection value from the ViewComponent
// to the main form and copy it to the input field
function copyInputTextTo(anchor) {
var root = anchor.closest('fieldset');
var target = document.getElementById("@Model.IdOfTargetInputField");
var source = root.querySelector('#customerSelect2') ;
if (source && target) {
target.value = source.value;
}
return false;
}
</script>
3.3 Ubicación de los archivos en el proyecto
4 Conclusión
Ver Componentes en ASP.NET8 es una forma superior de arquitectura de componentes, y lo encontré práctico. Se tarda un tiempo en acostumbrarse a ellos y aprender a crear / usarlos, pero definitivamente son útiles para cualquier expertopor ASP.NETEl programa.
5 Referencias
[1] Ver componentes en ASP.NET Core
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-9.0
[2] Ver componentes en ASP.NET Core MVC
https://dotnettutorials.net/lesson/view-components-in-asp-net-core-mvc/