Compartilhar via


Usar controladores e exibições para implementar uma interface do usuário de listagem/detalhes

pela Microsoft

Baixar PDF

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):

Captura de tela da janela Gerenciador de Soluções mostrando a pasta Controladores e os itens de menu Adicionar e Controlador realçados em azul.

Isso abrirá a caixa de diálogo "Adicionar Controlador":

Captura de tela da caixa de diálogo Adicionar Controlador mostrando o campo Nome do Controlador preenchido com o texto Controlador de Jantares.

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:

Captura de tela da janela Gerenciador de Soluções mostrando o arquivo dot cs dos Controladores de Jantar realçado em azul.

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:

Captura de tela da janela de resposta gerada com a execução do aplicativo NerdDinner, mostrando o texto Em breve: Jantares.

Digitar na URL "/Dinners/Details/2" fará com que nosso método Details() seja executado e envie de volta a seguinte resposta:

Captura de tela da janela de resposta gerada com a execução do aplicativo NerdDinner, mostrando o texto Detalhes do Jantar ID: 2.

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:

Captura de tela da janela Gerenciador de Soluções mostrando o arquivo Global dot a s a x realçado em azul e circulado em vermelho.

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:

Captura de tela do método auxiliar View com o texto Exibir Exibição de Resultado (nome da exibição de cadeia de caracteres, modelo de objeto).

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):

Captura de tela do projeto com o item de menu de clique com o botão direito do mouse Adicionar Modo de Exibição realçado em azul e circulado em vermelho.

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":

Captura de tela da janela Adicionar Modo de Exibição com o campo Nome da exibição definido como Não Encontrado, a caixa de página Selecionar master marcada e a ID do Titular do Local de Conteúdo definida como Conteúdo Principal.

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):

Captura de tela da hierarquia de pastas da janela Gerenciador de Soluções com o arquivo não encontrado ponto a s p x realçado em azul.

Ele também abrirá nosso novo modelo de exibição "NotFound.aspx" no editor de códigos:

Captura de tela da janela do editor de código com o arquivo s p x ponto a ponto não encontrado aberto no editor de código.

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":

Captura de tela da janela Meu Aplicativo MVC com o / Dinners / Details / 9999 U R L na caixa de endereço circulado em vermelho.

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):

Captura de tela da janela do editor de código mostrando o item de menu de clique com o botão direito do mouse Adicionar ponto de exibição ponto ponto realçado em vermelho.

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"):

Captura de tela da janela Adicionar Exibição com a lista suspensa Exibir conteúdo definida como Detalhes e a classe de dados Exibir definida como Nerd Dinner dot Models dot 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":

Captura de tela da janela Gerenciador de Soluções mostrando a hierarquia de pastas com a pasta Jantares realçada em azul.

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:

Captura de tela da janela de resposta do aplicativo mostrando / Jantares / Detalhes / 1 U R L circulado em vermelho na caixa de endereço.

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:

Captura de tela da janela do editor de código mostrando uma lista suspensa com o item Descrição realçado em azul.

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:

Captura de tela da janela de resposta do aplicativo mostrando a nova estilização da exibição Dot NET Futures.

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):

Captura de tela da janela Adicionar Exibição com o Nome de exibição definido como Índice, a caixa Criar um modo de exibição fortemente tipado marcada e a caixa de página Selecionar master marcada.

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:

Captura de tela da janela de resposta do aplicativo mostrando a lista de jantares em um layout de grade após a atualização Adicionar Exibição.

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:

Captura de tela da janela do editor de código mostrando um menu suspenso com o item Da lista de endereços realçado em uma caixa pontilhada cinza.

Quando chegamos à atualização na URL /Dinners em nosso navegador, nossa exibição atualizada agora tem a seguinte aparência:

Captura de tela da janela de resposta do aplicativo mostrando uma lista de jantares futuros após o comando atualizar.

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:

Captura de tela da janela do editor de código com o texto de bloco de classe e porcentagem realçado e circulado em vermelho.

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:

Captura de tela da janela de resposta do aplicativo que mostra a próxima lista de jantares com novos links correspondentes aos itens da lista.

Quando clicarmos em qualquer um dos Jantares na lista, navegaremos para ver detalhes sobre ele:

Captura de tela da janela de resposta do aplicativo que mostra o item de lista selecionado e os detalhes correspondentes a ele, conforme inserido no banco de dados.

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:

Captura de tela da janela Gerenciador de Soluções mostrando a hierarquia de pastas com a pasta Jantares realçada em um retângulo azul.

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).