ASP.NET MVC6 (vNext)–ViewComponents

En ASP.NET vNext se unifican MVC y WebApi en una nueva API llamada MVC6. Aunque MVC6 se parece a MVC5 no es compatible con ella, del mismo modo que WebApi se parece a MVC pero por debajo son muy distintas.

Ya hemos viso algunas de las novedades o cambios que trae MVC6 (temas de model binding, controladores POCO, …) y en este post vamos a explorar uno más: los ViewComponents.

Resumiendo: los ViewComponents sustituyen a las vistas parciales. Ya no existe este concepto en MVC6. De hecho, tampoco nos engañemos, desde razor la diferencia entre vistas parciales y vistas normales (a nivel del archivo .cshtml) es muy pequeña: se puede usar una vista parcial como vista normal tan solo cambiando el “return PartialView()” por un “return View()” (o viceversa). En el motor de vistas de ASPX eso no era así, ya que las vistas eran archivos .aspx y las vistas parciales eran archivos .ascx.

En Razor la única diferencia actual entre una vista parcial y una normal es que en la segunda se procesa el archivo de Layout (usualmente _Layout.cshtml) y en la primera no. Pero no es el archivo .cshtml quien determina si es vista normal o parcial. Es el ActionResult devuelto. Si devuelves un ViewResult el archivo .cshtml se procesará como vista normal. Si devuelves un PartialViewResult el archivo .cshtml se procesará como vista parcial.

El código “clásico” en MVC5 para tener una vista parcial era algo como:

  1. public ActionResult Child()
  2. {
  3.     return PartialView();
  4. }

El problema con este enfoque es que esta acción es enrutable, por lo que cualquiera puede ir a la URL que enrute esa acción (p. ej. Home/Child si suponemos HomeController) y recibirá el contenido HTML de la vista parcial. Eso, generalmente, no se desea (para algo la vista es parcial).

Para solventar esto, en MVC4 se añadió el atributo [ChildActionOnly] que evitaba que una acción se enrutase. Así si decoramos la acción con dicho atributo cuando el usuario navega a la URL que debería enrutar dicha acción recibirá un error:

image

La acción se puede invocar a través del helper Html.RenderAction:

  1. @{ Html.RenderAction("Child", "Home"); }

Nota: Se puede usar Html.Partial o Html.RenderPartial para renderizar una vista parcial directamente (sin pasar por un controlador). Eso es útil en el caso de que no haya lógica asociada a dicha vista parcial (si la hay, lo suyo es colocarla en la acción y usar Html.RenderAction).

Bueno… así tenemos las cosas hoy en día: básicamente colocamos las acciones “hijas” en un controlador (porque es donde podemos colocar lógica) pero luego las quitamos del sistema de enrutamiento (con [ChildActionOnly]) y las llamamos indicando directamente que acción y que controlador es.

Realmente ¿tiene sentido que las acciones hijas estén en un controlador? No. Porque la responsabilidad del controlador es, básicamente, responder a peticiones del navegador y eso no es una petición del navegador.

Así en MVC6 se elimina el concepto de vista parcial y el PartialViewResult, y se sustituye por el concepto de ViewComponent. Ahora lo que antes eran acciones hijas son clases propias que derivan de ViewComponent:

  1. [ViewComponent(Name = "Child")]
  2. public class ChildComponent : ViewComponent
  3. {
  4.     public async Task<IViewComponentResult> InvokeAsync()
  5.     {
  6.         return View();
  7.     }
  8. }

El atributo [ViewComponent] nos permite especificar el nombre que damos al componente. El siguiente paso es definir el método InvokeAsync que devuelve una Task<IViewComponentResult> con el resultado. La clase ViewComponent nos define el método View() que devuelve la vista asociada a dicho componente (de forma análoga al método View() de un controlador).

La ubicación por defecto de la vista asociada a un componente es /Views/Shared/Components/[NombreComponente]/Default.cshtml. Es decir en mi caso tengo el fichero Default.cshtml en /Views/Shared/Components/Child:

image

Por supuesto ahora tengo un sitio donde colocar la lógica (si la hubiera) de dicho componente: la propia clase ChildComponent.

Finalmente nos queda ver como renderizamos el componente. Ya no tenemos Html.RenderAction, si no que en su lugar usamos la propiedad Component que tienen las vistas de MVC6:

  1. @await Component.InvokeAsync("Child")

Simplemente le pasamos el nombre del componente (el mismo definido en el atributo [ViewComponent].

Y listos 🙂

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *