Usar controladores e exibições para implementar uma interface do usuário de listagem/detalhes
pela Microsoft
Esta é a etapa 4 de um tutorial gratuito de aplicativo "NerdDinner" que explica como criar um aplicativo Web pequeno, mas completo, usando ASP.NET MVC 1.
A etapa 4 mostra como adicionar um Controlador ao aplicativo que aproveita nosso modelo para fornecer aos usuários uma experiência de navegação de detalhes/listagem de dados para jantares em nosso site NerdDinner.
Se você estiver usando ASP.NET MVC 3, recomendamos que siga os tutoriais do Introdução With MVC 3 ou MVC Music Store.
Etapa 4 do NerdDinner: controladores e exibições
Com estruturas da Web tradicionais (ASP clássico, PHP, ASP.NET Web Forms etc.), as URLs de entrada normalmente são mapeadas para arquivos em disco. Por exemplo: uma solicitação para uma URL como "/Products.aspx" ou "/Products.php" pode ser processada por um arquivo "Products.aspx" ou "Products.php".
As estruturas MVC baseadas na Web mapeiam URLs para o código do servidor de uma maneira ligeiramente diferente. Em vez de mapear URLs de entrada para arquivos, eles mapeiam URLs para métodos em classes. Essas classes são chamadas de "Controladores" e são responsáveis por processar solicitações HTTP de entrada, lidar com a entrada do usuário, recuperar e salvar dados e determinar a resposta para enviar de volta ao cliente (exibir HTML, baixar um arquivo, redirecionar para uma URL diferente etc.).
Agora que criamos um modelo básico para nosso aplicativo NerdDinner, nossa próxima etapa será adicionar um Controlador ao aplicativo que o aproveita para fornecer aos usuários uma experiência de navegação de detalhes/listagem de dados para Jantares em nosso site.
Adicionando um controlador DinnersController
Começaremos clicando com o botão direito do mouse na pasta "Controladores" em nosso projeto Web e, em seguida, selecionamos o comando de menu Adicionar Controlador> (você também pode executar esse comando digitando Ctrl-M, Ctrl-C):
Isso abrirá a caixa de diálogo "Adicionar Controlador":
Vamos nomear o novo controlador como "DinnersController" e clicar no botão "Adicionar". Em seguida, o Visual Studio adicionará um arquivo DinnersController.cs em nosso diretório \Controllers:
Ele também abrirá a nova classe DinnersController no editor de códigos.
Adicionando métodos de ação Index() e Details() à classe DinnersController
Queremos permitir que os visitantes que usam nosso aplicativo naveguem por uma lista de jantares futuros e permitir que eles cliquem em qualquer Jantar na lista para ver detalhes específicos sobre ele. Faremos isso publicando as seguintes URLs de nosso aplicativo:
URL | Finalidade |
---|---|
/Jantares/ | Exibir uma lista HTML dos próximos jantares |
/Dinners/Details/[id] | Exiba detalhes sobre um jantar específico indicado por um parâmetro "id" inserido na URL – que corresponderá ao DinnerID do jantar no banco de dados. Por exemplo: /Dinners/Details/2 exibiria uma página HTML com detalhes sobre o Jantar cujo valor dinnerID é 2. |
Publicaremos implementações iniciais dessas URLs adicionando dois "métodos de ação" públicos à nossa classe DinnersController, como abaixo:
public class DinnersController : Controller {
//
// HTTP-GET: /Dinners/
public void Index() {
Response.Write("<h1>Coming Soon: Dinners</h1>");
}
//
// HTTP-GET: /Dinners/Details/2
public void Details(int id) {
Response.Write("<h1>Details DinnerID: " + id + "</h1>");
}
}
Em seguida, executaremos o aplicativo NerdDinner e usaremos nosso navegador para invocá-los. Digitar na URL "/Dinners/" fará com que nosso método Index() seja executado e enviará de volta a seguinte resposta:
Digitar na URL "/Dinners/Details/2" fará com que nosso método Details() seja executado e envie de volta a seguinte resposta:
Você deve estar se perguntando - como ASP.NET MVC sabia criar nossa classe DinnersController e invocar esses métodos? Para entender que vamos dar uma olhada rápida em como o roteamento funciona.
Noções básicas sobre ASP.NET roteamento MVC
ASP.NET MVC inclui um poderoso mecanismo de roteamento de URL que fornece muita flexibilidade no controle de como as URLs são mapeadas para classes de controlador. Ele nos permite personalizar completamente como ASP.NET MVC escolhe qual classe de controlador criar, qual método invocar nela, bem como configurar diferentes maneiras pelas quais as variáveis podem ser analisadas automaticamente da URL/Querystring e passadas para o método como argumentos de parâmetro. Ele oferece a flexibilidade de otimizar totalmente um site para SEO (otimização do mecanismo de pesquisa), bem como publicar qualquer estrutura de URL que desejamos de um aplicativo.
Por padrão, novos projetos do MVC ASP.NET vêm com um conjunto pré-configurado de regras de roteamento de URL já registradas. Isso nos permite começar facilmente em um aplicativo sem precisar configurar nada explicitamente. Os registros de regra de roteamento padrão podem ser encontrados na classe "Aplicativo" de nossos projetos , que podemos abrir clicando duas vezes no arquivo "Global.asax" na raiz do nosso projeto:
As regras de roteamento padrão ASP.NET MVC são registradas no método "RegisterRoutes" desta classe:
public void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL w/ params
new { controller="Home", action="Index",id="" } // Param defaults
);
}
As "rotas. A chamada de método MapRoute()" acima registra uma regra de roteamento padrão que mapeia URLs de entrada para classes de controlador usando o formato de URL: "/{controller}/{action}/{id}" – em que "controller" é o nome da classe do controlador para instanciar, "action" é o nome de um método público a ser invocado nele e "id" é um parâmetro opcional inserido na URL que pode ser passado como um argumento para o método . O terceiro parâmetro passado para a chamada de método "MapRoute()" é um conjunto de valores padrão a serem usados para os valores de controlador/ação/id caso não estejam presentes na URL (Controller = "Home", Action="Index", Id="").
Abaixo está uma tabela que demonstra como uma variedade de URLs são mapeadas usando a regra de rota padrão "/{controllers}/{action}/{id}":
URL | Classe Controller | Método de ação | Parâmetros passados |
---|---|---|---|
/Dinners/Details/2 | DinnersController | Details(id) | id=2 |
/Dinners/Edit/5 | DinnersController | Edit(id) | id=5 |
/Dinners/Create | DinnersController | Criar() | N/D |
/Jantares | DinnersController | Index() | N/D |
/Casa | Homecontroller | Index() | N/D |
/ | Homecontroller | Index() | N/D |
As últimas três linhas mostram os valores padrão (Controller = Home, Action = Index, Id = "") sendo usados. Como o método "Index" é registrado como o nome de ação padrão se um não for especificado, as URLs "/Dinners" e "/Home" fazem com que o método de ação Index() seja invocado em suas classes Controller. Como o controlador "Home" é registrado como o controlador padrão se um não for especificado, a URL "/" faz com que o HomeController seja criado e o método de ação Index() nele seja invocado.
Se você não gosta dessas regras de roteamento de URL padrão, a boa notícia é que elas são fáceis de alterar, basta editá-las dentro do método RegisterRoutes acima. No entanto, para nosso aplicativo NerdDinner, não vamos alterar nenhuma das regras de roteamento de URL padrão. Em vez disso, vamos usá-las no estado em que se encontra.
Usando o DinnerRepository de nosso DinnersController
Agora vamos substituir nossa implementação atual dos métodos de ação Index() e Details() do DinnersController por implementações que usam nosso modelo.
Usaremos a classe DinnerRepository que criamos anteriormente para implementar o comportamento. Começaremos adicionando uma instrução "using" que faz referência ao namespace "NerdDinner.Models" e, em seguida, declararemos uma instância do nosso DinnerRepository como um campo em nossa classe DinnerController.
Mais adiante neste capítulo, apresentaremos o conceito de "Injeção de Dependência" e mostraremos outra maneira de nossos Controladores obterem uma referência a um DinnerRepository que permite um melhor teste de unidade, mas, por enquanto, vamos apenas criar uma instância do nosso DinnerRepository embutido, como abaixo.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NerdDinner.Models;
namespace NerdDinner.Controllers {
public class DinnersController : Controller {
DinnerRepository dinnerRepository = new DinnerRepository();
//
// GET: /Dinners/
public void Index() {
var dinners = dinnerRepository.FindUpcomingDinners().ToList();
}
//
// GET: /Dinners/Details/2
public void Details(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
}
}
}
Agora estamos prontos para gerar uma resposta HTML de volta usando nossos objetos de modelo de dados recuperados.
Usando exibições com nosso controlador
Embora seja possível escrever código em nossos métodos de ação para montar HTML e, em seguida, usar o método auxiliar Response.Write() para enviá-lo de volta ao cliente, essa abordagem torna-se bastante difícil rapidamente. Uma abordagem muito melhor é executarmos apenas a lógica de aplicativo e dados dentro de nossos métodos de ação DinnersController e, em seguida, passar os dados necessários para renderizar uma resposta HTML para um modelo de "exibição" separado que é responsável por gerar a representação HTML dele. Como veremos em um momento, um modelo de "exibição" é um arquivo de texto que normalmente contém uma combinação de marcação HTML e código de renderização inserido.
Separar nossa lógica de controlador de nossa renderização de exibição traz vários grandes benefícios. Em particular, ele ajuda a impor uma clara "separação de preocupações" entre o código do aplicativo e o código de formatação/renderização da interface do usuário. Isso facilita muito a lógica do aplicativo de teste de unidade isoladamente da lógica de renderização da interface do usuário. Isso facilita a modificação posterior dos modelos de renderização da interface do usuário sem precisar fazer alterações no código do aplicativo. E isso pode facilitar a colaboração de desenvolvedores e designers em projetos.
Podemos atualizar nossa classe DinnersController para indicar que queremos usar um modelo de exibição para enviar de volta uma resposta de interface do usuário HTML alterando as assinaturas de método de nossos dois métodos de ação de ter um tipo de retorno "void" para, em vez disso, ter um tipo de retorno de "ActionResult". Em seguida, podemos chamar o método auxiliar View() na classe base Controller para retornar um objeto "ViewResult" como abaixo:
public class DinnersController : Controller {
DinnerRepository dinnerRepository = new DinnerRepository();
//
// GET: /Dinners/
public ActionResult Index() {
var dinners = dinnerRepository.FindUpcomingDinners().ToList();
return View("Index", dinners);
}
//
// GET: /Dinners/Details/2
public ActionResult Details(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
if (dinner == null)
return View("NotFound");
else
return View("Details", dinner);
}
}
A assinatura do método auxiliar View() que estamos usando acima tem a seguinte aparência:
O primeiro parâmetro para o método auxiliar View() é o nome do arquivo de modelo de exibição que desejamos usar para renderizar a resposta HTML. O segundo parâmetro é um objeto de modelo que contém os dados de que o modelo de exibição precisa para renderizar a resposta HTML.
Em nosso método de ação Index(), estamos chamando o método auxiliar View() e indicando que queremos renderizar uma listagem HTML de jantares usando um modelo de exibição "Index". Estamos passando ao modelo de exibição uma sequência de objetos Dinner para gerar a lista de:
//
// GET: /Dinners/
public ActionResult Index() {
var dinners = dinnerRepository.FindUpcomingDinners().ToList();
return View("Index", dinners);
}
Em nosso método de ação Details(), tentamos recuperar um objeto Dinner usando a ID fornecida na URL. Se um Jantar válido for encontrado, chamamos o método auxiliar View(), indicando que queremos usar um modelo de exibição "Detalhes" para renderizar o objeto Dinner recuperado. Se um jantar inválido for solicitado, renderizaremos uma mensagem de erro útil que indica que o Jantar não existe usando um modelo de exibição "NotFound" (e uma versão sobrecarregada do método auxiliar View() que usa apenas o nome do modelo):
//
// GET: /Dinners/Details/2
public ActionResult Details(int id) {
Dinner dinner = dinnerRepository.FindDinner(id);
if (dinner == null)
return View("NotFound");
else
return View("Details", dinner);
}
Agora, vamos implementar os modelos de exibição "NotFound", "Details" e "Index".
Implementando o modelo de exibição "NotFound"
Começaremos implementando o modelo de exibição "NotFound", que exibe uma mensagem de erro amigável indicando que o jantar solicitado não pode ser encontrado.
Criaremos um novo modelo de exibição posicionando nosso cursor de texto dentro de um método de ação do controlador e, em seguida, clicaremos com o botão direito do mouse e escolheremos o comando de menu "Adicionar Exibição" (também podemos executar esse comando digitando Ctrl-M, Ctrl-V):
Isso abrirá uma caixa de diálogo "Adicionar Exibição", como abaixo. Por padrão, a caixa de diálogo preencherá previamente o nome da exibição a ser criada para corresponder ao nome do método de ação em que o cursor estava quando a caixa de diálogo foi iniciada (neste caso, "Detalhes"). Como queremos primeiro implementar o modelo "NotFound", substituiremos esse nome de exibição e o definiremos como "NotFound":
Quando clicarmos no botão "Adicionar", o Visual Studio criará um novo modelo de exibição "NotFound.aspx" para nós no diretório "\Views\Dinners" (que também será criado se o diretório ainda não existir):
Ele também abrirá nosso novo modelo de exibição "NotFound.aspx" no editor de códigos:
Por padrão, os modelos de exibição têm duas "regiões de conteúdo" em que podemos adicionar conteúdo e código. O primeiro nos permite personalizar o "título" da página HTML enviada de volta. O segundo nos permite personalizar o "conteúdo main" da página HTML enviada de volta.
Para implementar nosso modelo de exibição "NotFound", adicionaremos um conteúdo básico:
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
Dinner Not Found
</asp:Content>
<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">
<h2>Dinner Not Found</h2>
<p>Sorry - but the dinner you requested doesn't exist or was deleted.</p>
</asp:Content>
Em seguida, podemos experimentá-lo no navegador. Para fazer isso, vamos solicitar a URL "/Dinners/Details/9999 ". Isso se referirá a um jantar que não existe atualmente no banco de dados e fará com que nosso método de ação DinnersController.Details() renderize nosso modelo de exibição "NotFound":
Uma coisa que você observará na captura de tela acima é que nosso modelo de exibição básico herdou um monte de HTML que envolve o conteúdo main na tela. Isso ocorre porque nosso modelo de exibição está usando um modelo de "página master" que nos permite aplicar um layout consistente em todos os modos de exibição no site. Discutiremos como master páginas funcionam mais em uma parte posterior deste tutorial.
Implementando o modelo de exibição "Detalhes"
Agora, vamos implementar o modelo de exibição "Detalhes", que gerará HTML para um único modelo dinner.
Faremos isso posicionando nosso cursor de texto dentro do método de ação Detalhes e, em seguida, clique com o botão direito do mouse e escolha o comando de menu "Adicionar Exibição" (ou pressione Ctrl-M, Ctrl-V):
Isso abrirá a caixa de diálogo "Adicionar Exibição". Manteremos o nome de exibição padrão ("Detalhes"). Também marcaremos a caixa de seleção "Criar um Modo de Exibição fortemente tipado" na caixa de diálogo e selecionaremos (usando a lista suspensa caixa de combinação) o nome do tipo de modelo que estamos passando do Controlador para o Modo de Exibição. Para esta exibição, estamos passando um objeto Dinner (o nome totalmente qualificado para esse tipo é: "NerdDinner.Models.Dinner"):
Ao contrário do modelo anterior, em que optamos por criar uma "Exibição Vazia", desta vez, optaremos por "scaffold" automaticamente a exibição usando um modelo "Detalhes". Podemos indicar isso alterando a lista suspensa "Exibir conteúdo" na caixa de diálogo acima.
"Scaffolding" gerará uma implementação inicial do nosso modelo de exibição de detalhes com base no objeto Dinner que estamos passando para ele. Isso fornece uma maneira fácil de começarmos rapidamente em nossa implementação de modelo de exibição.
Quando clicarmos no botão "Adicionar", o Visual Studio criará um novo arquivo de modelo de exibição "Details.aspx" para nós no diretório "\Views\Dinners":
Ele também abrirá nosso novo modelo de exibição "Details.aspx" no editor de código. Ele conterá uma implementação de scaffold inicial de uma exibição de detalhes com base em um modelo dinner. O mecanismo de scaffolding usa a reflexão do .NET para examinar as propriedades públicas expostas na classe passada e adicionará o conteúdo apropriado com base em cada tipo encontrado:
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
Details
</asp:Content>
<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">
<h2>Details</h2>
<fieldset>
<legend>Fields</legend>
<p>
DinnerID:
<%=Html.Encode(Model.DinnerID) %>
</p>
<p>
Title:
<%=Html.Encode(Model.Title) %>
</p>
<p>
EventDate:
<%= Html.Encode(String.Format("{0:g}", Model.EventDate)) %>
</p>
<p>
Description:
<%=Html.Encode(Model.Description) %>
</p>
<p>
HostedBy:
<%=Html.Encode(Model.HostedBy) %>
</p>
<p>
ContactPhone:
<%=Html.Encode(Model.ContactPhone) %>
</p>
<p>
Address:
<%=Html.Encode(Model.Address) %>
</p>
<p>
Country:
<%=Html.Encode(Model.Country) %>
</p>
<p>
Latitude:
<%= Html.Encode(String.Format("{0:F}",Model.Latitude)) %>
</p>
<p>
Longitude:
<%= Html.Encode(String.Format("{0:F}",Model.Longitude)) %>
</p>
</fieldset>
<p>
<%=Html.ActionLink("Edit","Edit", new { id=Model.DinnerID }) %>|
<%=Html.ActionLink("Back to List", "Index") %>
</p>
</asp:Content>
Podemos solicitar a URL "/Dinners/Details/1" para ver a aparência dessa implementação de scaffold "detalhes" no navegador. O uso dessa URL exibirá um dos jantares que adicionamos manualmente ao nosso banco de dados quando o criamos pela primeira vez:
Isso nos coloca em funcionamento rapidamente e nos fornece uma implementação inicial de nossa exibição Details.aspx. Em seguida, podemos ajustá-lo para personalizar a interface do usuário para nossa satisfação.
Quando examinarmos o modelo Details.aspx mais de perto, descobriremos que ele contém HTML estático, bem como código de renderização inserido. <% de> código nuggets executam código quando o modelo de exibição é renderizado e <%= %> de nuggets de código executam o código contido neles e, em seguida, renderizam o resultado para o fluxo de saída do modelo.
Podemos escrever código em nosso Modo de Exibição que acessa o objeto de modelo "Dinner" que foi passado de nosso controlador usando uma propriedade "Model" fortemente tipada. O Visual Studio nos fornece o intelliSense de código completo ao acessar essa propriedade "Model" no editor:
Vamos fazer alguns ajustes para que a origem do nosso modelo de exibição detalhes final fique como abaixo:
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
Dinner: <%=Html.Encode(Model.Title) %>
</asp:Content>
<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">
<h2><%=Html.Encode(Model.Title) %></h2>
<p>
<strong>When:</strong>
<%=Model.EventDate.ToShortDateString() %>
<strong>@</strong>
<%=Model.EventDate.ToShortTimeString() %>
</p>
<p>
<strong>Where:</strong>
<%=Html.Encode(Model.Address) %>,
<%=Html.Encode(Model.Country) %>
</p>
<p>
<strong>Description:</strong>
<%=Html.Encode(Model.Description) %>
</p>
<p>
<strong>Organizer:</strong>
<%=Html.Encode(Model.HostedBy) %>
(<%=Html.Encode(Model.ContactPhone) %>)
</p>
<%= Html.ActionLink("Edit Dinner", "Edit", new { id=Model.DinnerID })%> |
<%= Html.ActionLink("Delete Dinner","Delete", new { id=Model.DinnerID})%>
</asp:Content>
Quando acessarmos a URL "/Dinners/Details/1" novamente, ela será renderizada como abaixo:
Implementando o modelo de exibição "Index"
Agora, vamos implementar o modelo de exibição "Índice", que gerará uma listagem de jantares futuros. Para fazer isso, posicionaremos nosso cursor de texto dentro do método de ação Index e, em seguida, clicaremos com o botão direito do mouse e escolheremos o comando de menu "Adicionar Exibição" (ou pressionarEmos Ctrl-M, Ctrl-V).
Na caixa de diálogo "Adicionar Exibição", manteremos o modelo de exibição chamado "Índice" e marcaremos a caixa de seleção "Criar uma exibição fortemente tipada". Desta vez, optaremos por gerar automaticamente um modelo de exibição "Lista" e selecionar "NerdDinner.Models.Dinner" como o tipo de modelo passado para o modo de exibição (que, como indicamos que estamos criando um scaffold "List", a caixa de diálogo Adicionar Exibição assumirá que estamos passando uma sequência de objetos Dinner de nosso Controlador para o Modo de Exibição):
Quando clicarmos no botão "Adicionar", o Visual Studio criará um novo arquivo de modelo de exibição "Index.aspx" para nós no diretório "\Views\Dinners". Ele "fará scaffold" de uma implementação inicial dentro dele que fornece uma listagem de tabela HTML dos Jantares que passamos para o modo de exibição.
Quando executarmos o aplicativo e acessarmos a URL "/Dinners/", ele renderizará nossa lista de jantares da seguinte maneira:
A solução de tabela acima nos dá um layout semelhante à grade de nossos dados do Jantar , que não é exatamente o que queremos para o nosso consumidor voltado para a listagem do Jantar. Podemos atualizar o modelo de exibição Index.aspx e modificá-lo para listar menos colunas de dados e usar um <elemento ul> para renderizá-los em vez de uma tabela usando o código abaixo:
<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">
<h2>Upcoming Dinners</h2>
<ul>
<% foreach (var dinner in Model) { %>
<li>
<%=Html.Encode(dinner.Title) %>
on
<%=Html.Encode(dinner.EventDate.ToShortDateString())%>
@
<%=Html.Encode(dinner.EventDate.ToShortTimeString())%>
</li>
<% } %>
</ul>
</asp:Content>
Estamos usando o palavra-chave "var" dentro da instrução foreach acima enquanto percorremos cada jantar em nosso Modelo. Aqueles que não estão familiarizados com o C# 3.0 podem pensar que usar "var" significa que o objeto de jantar está atrasado. Em vez disso, isso significa que o compilador está usando inferência de tipo em relação à propriedade "Model" fortemente tipada (que é do tipo "IEnumerable<Dinner>") e compilando a variável local "dinner" como um tipo Dinner , o que significa que obtemos intelliSense completo e verificação em tempo de compilação para ele dentro de blocos de código:
Quando chegamos à atualização na URL /Dinners em nosso navegador, nossa exibição atualizada agora tem a seguinte aparência:
Isso está melhor – mas ainda não está totalmente lá. Nossa última etapa é permitir que os usuários finais cliquem em Jantares individuais na lista e veja detalhes sobre eles. Implementaremos isso renderizando elementos de hiperlink HTML que se vinculam ao método de ação Details em nosso DinnersController.
Podemos gerar esses hiperlinks em nossa exibição de Índice de duas maneiras. A primeira é criar manualmente um HTML <com elementos> como abaixo, em que inseremos <% de> blocos dentro de <um> elemento HTML:
Uma abordagem alternativa que podemos usar é aproveitar o método auxiliar interno "Html.ActionLink()" em ASP.NET MVC que dá suporte à criação programática de um> elemento HTML <que se vincula a outro método de ação em um Controlador:
<%= Html.ActionLink(dinner.Title, "Details", new { id=dinner.DinnerID }) %>
O primeiro parâmetro para o método auxiliar Html.ActionLink() é o link-text a ser exibido (nesse caso, o título do jantar), o segundo parâmetro é o nome da ação Controller para o qual queremos gerar o link (nesse caso, o método Details) e o terceiro parâmetro é um conjunto de parâmetros a serem enviados para a ação (implementado como um tipo anônimo com nome/valores de propriedade). Nesse caso, estamos especificando o parâmetro "id" do jantar ao qual desejamos vincular e, como a regra de roteamento de URL padrão no ASP.NET MVC é "{Controller}/{Action}/{id}", o método auxiliar Html.ActionLink() gerará a seguinte saída:
<a href="/Dinners/Details/1">.NET Futures</a>
Para nossa exibição Index.aspx, usaremos a abordagem do método auxiliar Html.ActionLink() e faremos cada jantar no link da lista para a URL de detalhes apropriada:
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
Upcoming Dinners
</asp:Content>
<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">
<h2>Upcoming Dinners</h2>
<ul>
<% foreach (var dinner in Model) { %>
<li>
<%=Html.ActionLink(dinner.Title, "Details", new { id=dinner.DinnerID }) %>
on
<%=Html.Encode(dinner.EventDate.ToShortDateString())%>
@
<%=Html.Encode(dinner.EventDate.ToShortTimeString())%>
</li>
<% } %>
</ul>
</asp:Content>
E agora, quando chegamos à URL /Dinners , nossa lista de jantares tem a seguinte aparência:
Quando clicarmos em qualquer um dos Jantares na lista, navegaremos para ver detalhes sobre ele:
Nomenclatura baseada em convenção e a estrutura do diretório \Views
ASP.NET aplicativos MVC por padrão usam uma estrutura de nomenclatura de diretório baseada em convenção ao resolver modelos de exibição. Isso permite que os desenvolvedores evitem ter que qualificar totalmente um caminho de localização ao referenciar exibições de dentro de uma classe Controller. Por padrão, ASP.NET MVC procurará o arquivo de modelo de exibição no diretório *\Views[ControllerName]* sob o aplicativo.
Por exemplo, estamos trabalhando na classe DinnersController , que referencia explicitamente três modelos de exibição: "Index", "Details" e "NotFound". ASP.NET MVC procurará por esses modos de exibição por padrão no diretório \Views\Dinners abaixo do diretório raiz do aplicativo:
Observe acima como atualmente há três classes de controlador dentro do projeto (DinnersController, HomeController e AccountController – as duas últimas foram adicionadas por padrão quando criamos o projeto) e há três subdiretórios (um para cada controlador) no diretório \Views.
As exibições referenciadas dos controladores Página Inicial e Contas resolve automaticamente seus modelos de exibição dos respectivos diretórios \Views\Home e \Views\Account. O subdiretório \Views\Shared fornece uma maneira de armazenar modelos de exibição que são reutilizadas em vários controladores dentro do aplicativo. Quando ASP.NET MVC tenta resolve um modelo de exibição, ele primeiro marcar dentro do diretório específico \Views[Controller] e, se não conseguir encontrar o modelo de exibição, ele examinará no diretório \Views\Shared.
Quando se trata de nomear modelos de exibição individuais, a orientação recomendada é que o modelo de exibição compartilhe o mesmo nome que o método de ação que fez com que ele fosse renderizado. Por exemplo, acima de nosso método de ação "Index" está usando a exibição "Index" para renderizar o resultado da exibição, e o método de ação "Detalhes" está usando a exibição "Detalhes" para renderizar seus resultados. Isso facilita a visualização rápida de qual modelo está associado a cada ação.
Os desenvolvedores não precisam especificar explicitamente o nome do modelo de exibição quando o modelo de exibição tiver o mesmo nome que o método de ação que está sendo invocado no controlador. Em vez disso, podemos apenas passar o objeto de modelo para o método auxiliar "View()" (sem especificar o nome da exibição) e ASP.NET MVC inferirá automaticamente que queremos usar o modelo de exibição \Views[ControllerName][ActionName] no disco para renderizá-lo.
Isso nos permite limpo o código do controlador um pouco e evitar duplicar o nome duas vezes em nosso código:
public class DinnersController : Controller {
DinnerRepository dinnerRepository = new DinnerRepository();
//
// GET: /Dinners/
public ActionResult Index() {
var dinners = dinnerRepository.FindUpcomingDinners().ToList();
return View(dinners);
}
//
// GET: /Dinners/Details/2
public ActionResult Details(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
if (dinner == null)
return View("NotFound");
else
return View(dinner);
}
}
O código acima é tudo o que é necessário para implementar uma boa experiência de listagem/detalhes do jantar para o site.
Próxima etapa
Agora temos uma boa experiência de navegação no jantar construída.
Agora vamos habilitar o suporte à edição de formulário de dados CRUD (Criar, Ler, Atualizar, Excluir).