Parte 3: Exibições e ViewModels
por Jon Galloway
O MVC Music Store é um aplicativo de tutorial que apresenta e explica passo a passo como usar ASP.NET MVC e Visual Studio para desenvolvimento na Web.
A MVC Music Store é uma implementação leve de loja de exemplo que vende álbuns de música online e implementa a administração básica do site, a entrada do usuário e a funcionalidade do carrinho de compras.
Esta série de tutoriais detalha todas as etapas executadas para criar o aplicativo de exemplo ASP.NET MVC Music Store. A parte 3 abrange Exibições e ViewModels.
Até agora, acabamos de retornar cadeias de caracteres de ações do controlador. Essa é uma boa maneira de ter uma ideia de como os controladores funcionam, mas não é assim que você deseja criar um aplicativo Web real. Vamos querer uma maneira melhor de gerar HTML de volta para navegadores que visitam nosso site – uma em que podemos usar arquivos de modelo para personalizar com mais facilidade o envio de conteúdo HTML de volta. É exatamente isso que o Views faz.
Adicionando um modelo de exibição
Para usar um modelo de exibição, alteraremos o método HomeController Index para retornar um ActionResult e fazer com que ele retorne View(), como abaixo:
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
A alteração acima indica que, em vez de retornar uma cadeia de caracteres, queremos usar uma "Exibição" para gerar um resultado de volta.
Agora, adicionaremos um modelo de Exibição apropriado ao nosso projeto. Para fazer isso, posicionaremos o cursor de texto dentro do método de ação Índice e, em seguida, clicaremos com o botão direito do mouse e selecionaremos "Adicionar Exibição". Isso abrirá a caixa de diálogo Adicionar Exibição:
A caixa de diálogo "Adicionar Exibição" nos permite gerar arquivos de modelo de exibição de forma rápida e fácil. Por padrão, a caixa de diálogo "Adicionar Exibição" preenche previamente o nome do modelo De exibição a ser criado para que ele corresponda ao método de ação que o usará. Como usamos o menu de contexto "Adicionar Exibição" dentro do método de ação Index() de nosso HomeController, a caixa de diálogo "Adicionar Exibição" acima tem "Index" como o nome de exibição pré-preenchido por padrão. Não precisamos alterar nenhuma das opções nessa caixa de diálogo, portanto, clique no botão Adicionar.
Quando clicarmos no botão Adicionar, o Desenvolvedor do Visual Web criará um novo modelo de exibição Index.cshtml para nós no diretório \Views\Home, criando a pasta se ainda não existir.
O nome e o local da pasta do arquivo "Index.cshtml" é importante e segue o padrão ASP.NET convenções de nomenclatura do MVC. O nome do diretório, \Views\Home, corresponde ao controlador , que é chamado de HomeController. O nome do modelo de exibição, Index, corresponde ao método de ação do controlador que exibirá a exibição.
ASP.NET MVC nos permite evitar a necessidade de especificar explicitamente o nome ou o local de um modelo de exibição quando usamos essa convenção de nomenclatura para retornar uma exibição. Por padrão, ele renderizará o modelo de exibição \Views\Home\Index.cshtml quando escrevermos código como abaixo em nosso HomeController:
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
O Visual Web Developer criou e abriu o modelo de exibição "Index.cshtml" depois que clicamos no botão "Adicionar" na caixa de diálogo "Adicionar Exibição". O conteúdo de Index.cshtml é mostrado abaixo.
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
Essa exibição está usando a sintaxe Razor, que é mais concisa do que o mecanismo de exibição Web Forms usado em ASP.NET Web Forms e versões anteriores do ASP.NET MVC. O mecanismo de exibição Web Forms ainda está disponível no ASP.NET MVC 3, mas muitos desenvolvedores acham que o mecanismo de exibição razor se encaixa ASP.NET desenvolvimento de MVC muito bem.
As três primeiras linhas definem o título da página usando ViewBag.Title. Veremos como isso funciona mais detalhadamente em breve, mas primeiro vamos atualizar o texto do título de texto e exibir a página. Atualize a <marca h2> para dizer "Esta é a Home Page", conforme mostrado abaixo.
@{
ViewBag.Title = "Index";
}
<h2>This is the Home Page</h2>
A execução do aplicativo mostra que nosso novo texto está visível na home page.
Usando um layout para elementos comuns do site
A maioria dos sites tem conteúdo que é compartilhado entre muitas páginas: navegação, rodapés, imagens de logotipo, referências de folha de estilos etc. O mecanismo de exibição razor facilita o gerenciamento usando uma página chamada _Layout.cshtml que foi criada automaticamente para nós dentro da pasta /Views/Shared.
Clique duas vezes nessa pasta para exibir o conteúdo, que são mostrados abaixo.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")"
rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")"
type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")"
type="text/javascript"></script>
</head>
<body>
@RenderBody()
</body>
</html>
O conteúdo de nossos modos de exibição individuais será exibido pelo @RenderBody() comando e qualquer conteúdo comum que queremos que apareça fora dele pode ser adicionado à marcação _Layout.cshtml. Queremos que nossa MVC Music Store tenha um cabeçalho comum com links para nossa home page e área da Loja em todas as páginas do site, portanto, adicionaremos isso ao modelo diretamente acima dessa @RenderBody() instrução.
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")"
rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")"
type="text/javascript"></script>
</head>
<body>
<div id="header">
<h1>
ASP.NET MVC MUSIC STORE</h1>
<ul id="navlist">
<li class="first"><a href="/"
id="current">Home</a></li>
<li><a
href="/Store/">Store</a></li>
</ul>
</div>
@RenderBody()
</body>
</html>
Atualizando o StyleSheet
O modelo de projeto vazio inclui um arquivo CSS muito simplificado que inclui apenas estilos usados para exibir mensagens de validação. Nosso designer forneceu alguns CSS adicionais e imagens para definir a aparência do nosso site, portanto, vamos adicioná-los agora.
O arquivo CSS atualizado e as Imagens estão incluídos no diretório Conteúdo do MvcMusicStore-Assets.zip que está disponível no MVC-Music-Store. Selecionaremos ambos no Windows Explorer e os soltaremos em nossa pasta Conteúdo da Solução no Desenvolvedor Web Visual, conforme mostrado abaixo:
Você será solicitado a confirmar se deseja substituir o arquivo Site.css existente. Clique em Sim.
A pasta Conteúdo do aplicativo agora aparecerá da seguinte maneira:
Agora, vamos executar o aplicativo e ver como estão nossas alterações na Página Inicial.
- Vamos examinar o que mudou: o método de ação Índice do HomeController encontrou e exibiu o modelo \Views\Home\Index.cshtmlView, mesmo que nosso código chamado "modo de exibição de retorno()", porque nosso modelo view seguiu a convenção de nomenclatura padrão.
- A Home Page está exibindo uma mensagem de boas-vindas simples definida no modelo de exibição \Views\Home\Index.cshtml.
- A Home Page está usando nosso modelo _Layout.cshtml e, portanto, a mensagem de boas-vindas está contida no layout HTML do site padrão.
Usando um modelo para passar informações para nossa exibição
Um modelo de exibição que apenas exibe HTML codificado não criará um site muito interessante. Para criar um site dinâmico, queremos passar informações de nossas ações do controlador para nossos modelos de exibição.
No padrão Model-View-Controller, o termo Modelo refere-se a objetos que representam os dados no aplicativo. Muitas vezes, os objetos de modelo correspondem a tabelas no banco de dados, mas não precisam.
Métodos de ação do controlador que retornam um ActionResult podem passar um objeto de modelo para a exibição. Isso permite que um Controlador empacote de forma limpa todas as informações necessárias para gerar uma resposta e, em seguida, passe essas informações para um modelo de exibição a ser usado para gerar a resposta HTML apropriada. Isso é mais fácil de entender vendo-o em ação, então vamos começar.
Primeiro, criaremos algumas classes de modelo para representar Gêneros e Álbuns em nossa loja. Vamos começar criando uma classe De gênero. Clique com o botão direito do mouse na pasta "Modelos" em seu projeto, escolha a opção "Adicionar Classe" e nomeie o arquivo "Genre.cs".
Em seguida, adicione uma propriedade Name de cadeia de caracteres pública à classe que foi criada:
public class Genre
{
public string Name { get; set; }
}
Observação: caso esteja se perguntando, a notação { get; set; } está fazendo uso do recurso de propriedades autoimplementadas do C#. Isso nos dá os benefícios de uma propriedade sem exigir que declaremos um campo de apoio.
Em seguida, siga as mesmas etapas para criar uma classe Album (chamada Album.cs) que tenha uma propriedade Title e Genre:
public class Album
{
public string Title { get; set; }
public Genre Genre { get; set; }
}
Agora podemos modificar o StoreController para usar Exibições que exibem informações dinâmicas do nosso Modelo. Se , para fins de demonstração agora , nomeássemos nossos Álbuns com base na ID da solicitação, poderíamos exibir essas informações como na exibição abaixo.
Começaremos alterando a ação Detalhes da Loja para que ela mostre as informações de um único álbum. Adicione uma instrução "using" à parte superior da classe StoreControllers para incluir o namespace MvcMusicStore.Models, portanto, não precisamos digitar MvcMusicStore.Models.Album sempre que quisermos usar a classe de álbum. A seção "usings" dessa classe agora deve aparecer como abaixo.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcMusicStore.Models;
Em seguida, atualizaremos a ação Controlador de detalhes para que ela retorne um ActionResult em vez de uma cadeia de caracteres, como fizemos com o método Index do HomeController.
public ActionResult Details(int id)
Agora podemos modificar a lógica para retornar um objeto Album à exibição. Posteriormente neste tutorial, recuperaremos os dados de um banco de dados, mas, por enquanto, usaremos "dados fictícios" para começar.
public ActionResult Details(int id)
{
var album = new Album { Title = "Album " + id };
return View(album);
}
Observação: se você não estiver familiarizado com C#, pode presumir que usar var significa que nossa variável de álbum está associada tardiamente. Isso não está correto – o compilador C# está usando a inferência de tipo com base no que estamos atribuindo à variável para determinar que o álbum é do tipo Álbum e compilar a variável de álbum local como um tipo de álbum, portanto, obtemos verificação em tempo de compilação e suporte ao editor de código do Visual Studio.
Agora vamos criar um modelo de exibição que usa nosso Álbum para gerar uma resposta HTML. Antes de fazermos isso, precisamos criar o projeto para que a caixa de diálogo Adicionar Exibição saiba sobre nossa classe de Álbum recém-criada. Você pode criar o projeto selecionando o item de menu Depurar⇨Criar MvcMusicStore (para crédito extra, você pode usar o atalho Ctrl-Shift-B para compilar o projeto).
Agora que configuramos nossas classes de suporte, estamos prontos para criar nosso modelo de Exibição. Clique com o botão direito do mouse no método Detalhes e selecione "Adicionar Exibição..." no menu de contexto.
Vamos criar um novo modelo de Exibição como fizemos antes com o HomeController. Como o estamos criando do StoreController, por padrão, ele será gerado em um arquivo \Views\Store\Index.cshtml.
Ao contrário de antes, vamos marcar caixa de seleção "Criar um modo de exibição fortemente tipado". Em seguida, selecionaremos nossa classe "Álbum" na lista suspensa "Exibir classe de dados". Isso fará com que a caixa de diálogo "Adicionar Exibição" crie um modelo de Exibição que espera que um objeto Album seja passado para ele a ser usado.
Quando clicarmos no botão "Adicionar", nosso modelo de exibição \Views\Store\Details.cshtml será criado, contendo o código a seguir.
@model MvcMusicStore.Models.Album
@{
ViewBag.Title = "Details";
}
<h2>Details</h2>
Observe a primeira linha, que indica que essa exibição é fortemente tipada para nossa classe Album. O mecanismo de exibição razor entende que ele foi passado por um objeto Album, para que possamos acessar facilmente as propriedades do modelo e até mesmo ter o benefício do IntelliSense no editor do Visual Web Developer.
Atualize a <marca h2> para que ela exiba a propriedade Título do Álbum modificando essa linha para aparecer da seguinte maneira.
<h2>Album: @Model.Title</h2>
Observe que o IntelliSense é disparado quando você entra no período após o @Model palavra-chave, mostrando as propriedades e os métodos aos quais a classe Album dá suporte.
Agora vamos executar novamente nosso projeto e visitar a URL /Store/Details/5. Veremos detalhes de um Álbum, como abaixo.
Agora, faremos uma atualização semelhante para o método de ação Procurar na Loja. Atualize o método para que ele retorne um ActionResult e modifique a lógica do método para que ele crie um novo objeto Genre e o retorne ao View.
public ActionResult Browse(string genre)
{
var genreModel = new Genre { Name = genre };
return View(genreModel);
}
Clique com o botão direito do mouse no método Browse e selecione "Adicionar Exibição..." no menu de contexto, adicione um Modo de Exibição fortemente tipado e adicione um fortemente tipado à classe Genre.
Atualize o <elemento h2> no código de exibição (em /Views/Store/Browse.cshtml) para exibir as informações de Gênero.
@model MvcMusicStore.Models.Genre
@{
ViewBag.Title = "Browse";
}
<h2>Browsing Genre: @Model.Name</h2>
Agora vamos executar novamente nosso projeto e navegar até /Store/Procurar? Genre=Disco URL. Veremos a página Procurar exibida como abaixo.
Por fim, vamos fazer uma atualização um pouco mais complexa para o método de ação Índice da Loja e exibir para exibir uma lista de todos os Gêneros em nossa loja. Faremos isso usando uma Lista de Gêneros como nosso objeto modelo, em vez de apenas um único Gênero.
public ActionResult Index()
{
var genres = new List<Genre>
{
new Genre { Name = "Disco"},
new Genre { Name = "Jazz"},
new Genre { Name = "Rock"}
};
return View(genres);
}
Clique com o botão direito do mouse no método de ação Índice da Loja e selecione Adicionar Exibição como antes, selecione Gênero como a classe Modelo e pressione o botão Adicionar.
Primeiro, alteraremos a @model declaração para indicar que a exibição estará esperando vários objetos Genre em vez de apenas um. Altere a primeira linha de /Store/Index.cshtml para ler da seguinte maneira:
@model IEnumerable<MvcMusicStore.Models.Genre>
Isso informa ao mecanismo de exibição razor que ele trabalhará com um objeto de modelo que pode conter vários objetos Genre. Estamos usando um Gênero IEnumerable<em vez de um Gênero> de Lista<, pois ele é mais genérico, permitindo que alteremos nosso tipo de modelo posteriormente para qualquer tipo de objeto que dê suporte à interface IEnumerable.>
Em seguida, vamos percorrer os objetos Genre no modelo, conforme mostrado no código de exibição concluído abaixo.
@model IEnumerable<MvcMusicStore.Models.Genre>
@{
ViewBag.Title = "Store";
}
<h3>Browse Genres</h3>
<p>
Select from @Model.Count()
genres:</p>
<ul>
@foreach (var genre in Model)
{
<li>@genre.Name</li>
}
</ul>
Observe que temos suporte completo do IntelliSense à medida que inserimos esse código, para que quando digitamos "@Model". Vemos todos os métodos e propriedades compatíveis com um IEnumerable do tipo Genre.
Em nosso loop "foreach", o Visual Web Developer sabe que cada item é do tipo Gênero, portanto, vemos o IntelliSense para cada tipo de Gênero.
Em seguida, o recurso de scaffolding examinou o objeto Genre e determinou que cada um terá uma propriedade Name, para que ele faça um loop e os grave. Ele também gera links editar, detalhes e excluir para cada item individual. Vamos aproveitar isso mais tarde em nosso gerente de loja, mas por enquanto gostaríamos de ter uma lista simples em vez disso.
Quando executamos o aplicativo e navegamos até /Store, vemos que a contagem e a lista de Gêneros são exibidas.
Adicionando links entre páginas
Nossa URL da /Store que lista gêneros atualmente lista os nomes de Gênero simplesmente como texto sem formatação. Vamos alterar isso para que, em vez de texto sem formatação, tenhamos os nomes de gênero vinculados à URL apropriada /Store/Browse, para que clicar em um gênero musical como "Disco" navegue até a URL /Store/Browse?genre=Disco. Podemos atualizar nosso modelo de exibição \Views\Store\Index.cshtml para gerar esses links usando código como abaixo (não digite isso – vamos melhorar isso):
<ul>
@foreach (var genre in Model)
{
<li><a href="/Store/Browse?genre=@genre.Name">@genre.Name</a></li>
}
</ul>
Isso funciona, mas pode causar problemas mais tarde, pois depende de uma cadeia de caracteres codificada. Por exemplo, se quiséssemos renomear o Controlador, precisaríamos pesquisar nosso código procurando links que precisam ser atualizados.
Uma abordagem alternativa que podemos usar é aproveitar um método auxiliar HTML. ASP.NET MVC inclui métodos auxiliares HTML que estão disponíveis em nosso código de modelo de exibição para executar uma variedade de tarefas comuns assim. O método auxiliar Html.ActionLink() é particularmente útil e facilita a criação de links> HTML <e cuida de detalhes irritantes, como garantir que os caminhos de URL sejam codificados corretamente em URL.
Html.ActionLink() tem várias sobrecargas diferentes para permitir a especificação do máximo de informações necessárias para seus links. No caso mais simples, você fornecerá apenas o texto do link e o método Action para acessar quando o hiperlink for clicado no cliente. Por exemplo, podemos vincular ao método "/Store/" Index() na página Detalhes da Loja com o texto do link "Ir para o Índice da Loja" usando a seguinte chamada:
@Html.ActionLink("Go
to the Store Index", "Index")
Observação: nesse caso, não precisamos especificar o nome do controlador porque estamos apenas vinculando a outra ação dentro do mesmo controlador que está renderizando a exibição atual.
No entanto, nossos links para a página Procurar precisarão passar um parâmetro, portanto, usaremos outra sobrecarga do método Html.ActionLink que usa três parâmetros:
-
- Texto do link, que exibirá o nome do gênero
-
- Nome da ação do controlador (Procurar)
-
- Valores de parâmetro de rota, especificando o nome (Gênero) e o valor (Nome do gênero)
Juntando tudo isso, veja como escreveremos esses links para a exibição Índice do Repositório:
<ul>
@foreach (var genre in Model)
{
<li>@Html.ActionLink(genre.Name,
"Browse", new { genre = genre.Name })</li>
}
</ul>
Agora, quando executarmos nosso projeto novamente e acessarmos a URL /Store/, veremos uma lista de gêneros. Cada gênero é um hiperlink – quando clicado, ele nos levará à URL /Store/Browse?genre=[gênero] .
O HTML para a lista de gêneros tem esta aparência:
<ul>
<li><a href="/Store/Browse?genre=Disco">Disco</a>
</li>
<li><a href="/Store/Browse?genre=Jazz">Jazz</a>
</li>
<li><a href="/Store/Browse?genre=Rock">Rock</a>
</li>
</ul>