Agregando una vista a nuestra aplicación ASP.NET Core MVC

Ahora vamos a continuar donde dejamos en el tutorial de ASP.NET MVC.

En esta sección vamos a modificar la clase HelloWorldController para que utilice archivos de plantilla de vista Razor y de esta manera encapsular el proceso de generar respuestas HTML al cliente.

Creamos archivos de plantilla usando Razor. Los archivos de plantilla Razor tienen la extensión .cshtml (si usamos C#). Proveen una forma elegante de crear una salida HTML usando C#.

Si repasamos lo que vimos en la introducción recordaremos que son archivos HTML comunes solo que tienen algunas expresiones adicionales más que son las que agrega el formato de plantillas Razor.

Actualmente el método Index retorna una cadena con un mensaje que está hardcodeado en la clase de controlador. En las clase HelloWorldController, reemplaza el método Index con el siguiente código.

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers
public IActionResult Index()
{
    return View();
}

El código anterior retorna un objeto View. Esto significa que vamos a usar una plantilla para generar la respuesta HTML al navegador. Los métodos del controlador (también conocidos como métodos de acción) tales como el método Index de arriba, no suelen retornas cadenas de texto simple, generalmente retornan un IActionResult (o una clase derivada de ActionResult).

  • Hagamos click derecho en la carpeta Views, elijamos Agregar > Nueva carpeta y le pongamos de nombre a la carpeta HelloWorld.
  • Hagamos click derecho en la carpeta HelloWold, y elijamos Agregar > Nuevo Elemento.
  • En el cuadro de dialogo de Agregar nuevo elemento
    • En el campo de búsqueda a la derecha arriba, escribamos vista o view si lo tenemos en inglés.
    • Elijamos Vista de Razor
    • En el campo de Nombre, cambiemos si es necesario a Index.cshtml.
    • Hagamos click en Agregar

Agregar nueva vista

Reemplacemos los contenidos de la vista Razor ubicada en el archivo Views/HelloWorld/Index.cshtml con lo siguiente:

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>Hello from our View Template!</p>

Naveguemos a http://localhost:xxxx/HelloWold. El método Index en la clase HelloWorldController no hizo mucho, ejecutó la sentencia return View(); lo cual especifica que el método debería utilizar un archivo de plantilla para generar la respuesta al navegador. Porque no especificamos en nombre del archivo de vista, MVC buscó el archivo Index.cshtml en la carpeta /Views/HelloWorld. La imagen de abajo muestra la cadena “Hello from our View Template!” hardcodeada en el archivo cshtml de la vista.

Recordemos que MVC funciona por convención, en este caso el nombre de archivo cshtml que va a buscar es igual al nombre del método del controlador (Index en este caso).

Usando vistas

Si la ventana del navegador es pequeña (por ejemplo en un dispositivo móvil), puede que necesitemos tocar en el botón de navegación de Bootstrap arriba de la derecha para ver los links Home, About y Contact.

Menú movil resaltado

Cambiando las vistas y las páginas de diseño

Pulsemos en los enlaces del menú(MVCMovie, Home, About). Cada página muestra el mismo diseño. El diseño del menú está implementado en el archivo Views/Shared/_Layout.cshtml. Abramos el archivo Views/Shared/_Layout.cshtml.

Las plantillas de diseño (en inglés Layout) nos permite especificar en un solo lugar el diseño del HTML que van a contener todas las páginas de nuestro sitio/app. Busquemos la línea RenderBody(). RenderBody() es un marcador de posición donde se va a generar todo el contenido de cada página de vista particular (como la Index.cshtml). Por ejemplo, si pulsamos en el enlace About, el contenido del archivo Views/Home/About.cshtml se va a generar donde está ubicado el método RenderBody()

Cambiemos el título del menu y los enlaces en el archivo de diseño

En el elemento título, cambiemos de el texto MvcMovie a Movie App. Cambiemos el texto del enlace en el menú de MvcMovie a Movie App y el controlador de Home a Movies como se muestra debajo:

Nota: La versión de ASP.NET Core 2.0 es algo diferente. No contiene @inject ApplicationInisights y @Html.Raw(JavaScriptSnippet.FullScript).

@inject Microsoft.ApplicationInsights.AspNetCore.JavaScriptSnippet JavaScriptSnippet
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Movie App</title>

    <environment names="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
        <link rel="stylesheet" href="~/css/site.css" />
    </environment>
    <environment names="Staging,Production">
        <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
              asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
        <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
    </environment>
    @Html.Raw(JavaScriptSnippet.FullScript)
</head>
<body>
    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a asp-area="" asp-controller="Movies" asp-action="Index" class="navbar-brand">Movie App</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
                    <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
                    <li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li>
                </ul>
            </div>
        </div>
    </nav>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; 2017 - MvcMovie</p>
        </footer>
    </div>

    <environment names="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/js/site.js" asp-append-version="true"></script>
    </environment>
    <environment names="Staging,Production">
        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                asp-fallback-test="window.jQuery"
                crossorigin="anonymous"
                integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk">
        </script>
        <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
                crossorigin="anonymous"
                integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
        </script>
        <script src="~/js/site.min.js" asp-append-version="true"></script>
    </environment>

    @RenderSection("Scripts", required: false)
</body>
</html>

**Advertencia: ** Todavía no hemos implementado el controlador Movies aún, así que si hacemos click en ese enlace vamos a tener un error 404 (no encontrado).

Guardemos los cambios y pulsemos en el enlace About. Notemos como el título ahora dice About - Movie App en lugar de About - MVC Movie

About desde la plantilla de vista

Pulsemos el enlace Contact y notemos que el título y texto del enlace también ahora muestran Movie App. Hemos sido capaces de cambiar una sola vez la plantilla de diseño y todas las páginas del sitio reflejan el cambio en el texto del enlace y en el título.

Las plantillas de diseños es el equivalente en MVC a las Master Pages.

Examinemos el archivo Views/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

El archivo Views/_ViewStart.cshtml: es lo que configura Views/Shared/_Layout.cshtml como plantilla de diseño de cada archivo de vista individual. Podemos utilizar la propiedad Layout para configurar una plantilla de diseño diferente, o configurarla a null para no tener ninguna plantilla de diseño.

Cambiemos el título de la vista Index.

Abrimos Views/HelloWorld/Index.cshtml. Hay dos lugares para hacer el cambio:

  • El texto que aparece en el título del navegador
  • El encabezado secundario (elemento <h2>)

Vamos a hacerlos ligeramente diferente para que veamos cual es el pedazo de código que cambia cada parte de la aplicación.

@{
    ViewData["Title"] = "Movie List";
}

<h2>My Movie List</h2>

<p>Hello from our View Template!</p>

La parte ViewData["Title"] = "Movie List"; en el código de arriba configura la propiedad Title del diccionario View Data con el valor “Movie List”. La propiedad Title es usada en el elemento HTML <title> en la plantilla de diseño.

<title>@ViewData["Title"] - Movie App</title>

Guardemos los cambios y naveguemos a http://localhost:xxxx/HelloWorld. Notemos que el título del navegador, el encabezado primario y el encabezado secundario han cambiado. (Si no vemos los cambios en el navegador puede que estmos viendo contenido cacheado. Presionemos Ctrl+F5 en el navegador para forzar que se recargue la página desde el servidor.) El título del navegador es creado con ViewData["Title"] que pusimos en el archivo de vista *Index.cshtml** y el “- Movie App” adicional que agregamos en la plantilla de diseño (Layout.cshtml).

Tambiemos notemos que el contenido del archivo de vista Index.cshtml se ha mezclado con el contenido de la plantilla de diseño Views/Shared/_Layout.cshtml y como todo se manda como una única respuesta HTML hacia el navegador. Las plantillas de diseño nos permiten cambiar de manera facil algo y que esos cambios se apliquen en todas las páginas de nuestra aplicación.

Hay información adicional en inglés en la página de Microsoft

Vista hello world

Nuestros pedacitos de “datos” (en este caso el mensaje “Hello from our View Template!”) está hardcodeado. La aplicación MVC tiene una “V” de Vista y tenemos una “C” de Controlador, pero todavía no tenemos la “M” de módelo.

Pasando datos del Controlador a la Vista

La acciones del controlador se invocan en respuesta a una petición de una URL. La clase del controlador es donde escribimos el código que maneja cada una de esas peticiones de entrada hechas por los navegadores web.

El controlador obtiene datos desde una fuente de datos y decide cual es el tipo de respuesta que le va a enviar al navegador. Los archivos de vista cshtml pueden ser usados por el controlador para generar y formatear una respuesta HTML para el ser mandada al navegador web.

Los controladores son los responsables de proveer los datos requeridos por un archivo de vista cshtml para generar una respuesta. Los archivos de vista cshtml no deberían tener ninguna lógica de diseño o interactuar con la base de datos de manera directa. En lugar de eso, los archivos de vista deberían funcionar solamente con los datos que reciban del controlador. Mantener esta “separación de responsabilidades” nos ayuda a mantener nuestro código limpio, testeable y mantenible.

Actualmente, el método Welcome en la clase HelloWorldController toma los parámetros name y ID y emite los valores directamente al navegador. En lugar de tener al controlador generando una respuesta como un string, cambiemos el controlador para usar un archivo de vista. Esta plantilla de vista genera una respuesta dinámica, lo cual requiere que los datos apropiados sean pasados desde el controlador a la vista para generar la respuesta. Hagamos esto haciendo que el controlador ponga los datos dinámicos (parámetros) que la vista necesita en el diccionario ViewData al cual la vista puede acceder.

Volvamos al archivo HelloWorldController.cs y cambiemos el método Welcome para que agregue valores para Message y NumTimes al diccionario ViewData. El diccionario ViewData es un objeto dinámico, lo que quiere decir que puedes poner lo que quieras en él; el objeto ViewData no tiene propiedades definidas hasta que pongamos algo en él. El sistema de binding de MVC automáticamente mapea los parámetros con nombre (name y numTimes) de la query string en la barra de direcciones a los parámetros de nuestro método. El archivo completo HelloWorldController.cs se se muestra a continuación.

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers
{
    public class HelloWorldController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Welcome(string name, int numTimes = 1)
        {
            ViewData["Message"] = "Hello " + name;
            ViewData["NumTimes"] = numTimes;

            return View();
        }
    }
}

El diccionario ViewData contiene todos los datos que serán pasados a la vista.

Creemos un archivo de vista para Welcome en la siguiente ruta Views/HelloWorld/Welcome.cshtml.

Vamos a crear un bucle en el archivo Welcome.cshtml que muestre el mensaje “Hello [name]” el número de veces definida por el parámetro NumTimes.

Reemplacemos los contenidos de Views/HelloWorld/Welcome.cshtml con los siguientes:

@{
    ViewData["Title"] = "Welcome";
}

<h2>Welcome</h2>

<ul>
    @for (int i = 0; i < (int)ViewData["NumTimes"]; i++)
    {
        <li>@ViewData["Message"]</li>
    }
</ul>

Guardemos los cambios y entremos en la siguiente dirección

http://localhost:xxxx/HelloWorld/Welcome?name=Rick&numtimes=4

Los datos son tomados de la URL y son pasados al controlador de manera automática utilizando el binder de MVC. El controlador empaca los datos en un diccionario ViewData y le pasa este objeto a la vista.

La vista genera los datos como HTML y los envía al navegador.

Pagina dice Hello Rick muchas veces

En el ejemplo de arriba, utilizamos el diccionario ViewData para pasar datos del controlador a la vista. Después en esta serie de tutoriales, vamos a usar un modelo para pasar datos del controlador a la vista.

Pasar datos usando un modelo es siempre preferible antes que usar el diccionario ViewData. Ver este enlace para más información.

Si bien ahora tenemos una especie de modelo, todavía no viene de la base de datos. Tomemos lo que aprendimos y creemos una base de datos de películas en la siguiente parte del tutorial.