ASP .NET - MVC Music Store - Definindo Views e ViewsModels


Até agora incluímos dois controladores em nosso projeto que retornam apenas strings em nossas Actions. Essa é uma boa maneira de ter uma ideia de como os controladores trabalham, mas não representa uma funcionalidade real da nossa loja de música on-line.

O que desejamos é uma maneira melhor de gerar HTML para os navegadores que visitam o nosso site, onde podemos usar arquivos de modelo para customizar o conteúdo HTML enviado de volta.

Incluindo um template View

Para usar um template View, vamos alterar o método Index() da classe HomeController para devolver um tipo ActionResult, retornando uma View(), como abaixo:

   ' GET: /Home/
    Public Function Index() As ActionResult
         Return View()
   End Function

A mudança acima indica que em vez de retornar uma string, vamos usar uma View() para gerar o resultado esperado.

Vamos agora adicionar um template View() adequado ao nosso projeto. Para fazer isso vamos posicionar o cursor de texto dentro do método de ação de Index(), em seguida, usando o botão direito do mouse e selecionar "Add View". Isso fará com que a caixa de diálogo Add View seja exibida:

No diálogo "Add View" podemos facilmente gerar arquivos usando o template View. Por padrão a opção "Add View" preenche o nome do modelo View de modo que combine o método Action que irá usá-lo.

Como nós usamos o menu "Add View" de contexto dentro do método de ação Index() do nosso HomeController, o diálogo "Add View" tem "Index", como o nome da view pré-preenchida por padrão. Nós não precisamos alterar qualquer uma das opções desta caixa de diálogo, então clique no botão Add.

Ao clicar no botão Add, iremos criar um novo template view Index.vbhtml no diretório \Views\Home, criando a pasta se ela não existir.

O nome e a localização da pasta do arquivo "Index.vbhtml" é importante e segue o padrão de convenção de nomenclatura ASP.NET MVC. O nome do diretório \Views\Home, corresponde ao controlador que é chamado HomeController. O nome do template View, Index, corresponde ao método Action do controlador que vai estar exibindo a view.

A ASP.NET MVC nos libera de ter que especificar explicitamente o nome ou a localização de um template View quando usamos essa convenção de nomenclatura para retornar uma view. Por padrão ele renderiza o template view \Views\Home\Index.vbhtml quando formos escrever o código como abaixo dentro de nossa HomeController.

A view esta usando a sintaxe Razor, que é mais concisa do que o mecanismo de exibição de formulários da Web usado em ASP.NET Web Forms e versões anteriores do ASP.NET MVC. Um 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 muito bem ao desenvolvimento ASP.NET.

Obs: Para detalhes sobre a sintaxe Razor veja o meu artigo: ASP .NET - Apresentando a sintaxe Razor - Macoratti.net

As três primeiras linhas definem o título da página usando ViewBag.Title. Veremos como isso funciona em detalhes em breve, mas primeiro vamos atualizar o texto do cabeçalho e a view da página. Atualize a tag <h2> alterando o texto para "Esta é minha Home Page", como mostrado abaixo.

@Code
    ViewData("Title") = "Index"
End Code

<h2>Esta é minha Home Page</h2>

Executando a aplicação teremos o resultado abaixo:

Usando um layout comum

A maioria dos sites tem conteúdo que é compartilhado entre muitas páginas: navegação, rodapés, imagens de logotipo, referências, stylesheet, etc. O mecanismo view Razor torna isso fácil de gerenciar utilizando uma página chamada _Layout.cshtml que foi automaticamente criada para nós dentro da pasta /Views/Shared:

O conteúdo das nossas views individuais será exibido pelo comando @RenderBody(), e qualquer conteúdo comum que queremos exibir poderá ser adicionado à marcação _Layout.cshtml.

Desejamos que a nossa Loja de Música MVC tenha um cabeçalho comum com links para nossa página inicial e para a área da loja em todas as páginas do site, por isso vamos acrescentar o conteúdo para isso ao modelo logo acima da declaração @RenderBody() :

Atualizando as folhas de estilo

O template ASP .NET MVC 3 Web Application gera arquivo de estilho chamado Site.css na pasta Content que é muito simples e que contém apenas estilos usados para exibir mensagens de validações.

O nosso site web para a loja de música utiliza alguns estilos e imagens adicionais para definir um visual mais adequado.

Vamos então atualizar o arquivo CSS e o conteúdo da pasta Content incluindo uma pasta Images e incluindo as imagens em nosso projeto:

Após isso vamos rodar novamente nossa aplicação e ver o resultado obtido:

Melhorou um pouco , não é mesmo ???

Vamos rever o que mudou:

Usando um Model para passar informação para nossa View

Um template View que apenas exiba HTML codificado não vai tornar o nosso site muito interessante. Para criar um site web dinâmico, vamos querer passar informações de nossas ações do controlador para os nossos modelos de exibição: as views.

No padrão Model-View-Controller, o termo Model refere-se a objetos que representam os dados no aplicativo. Muitas vezes, mas não obrigatoriamente, os objetos do Model correspondem às tabelas em um banco de dados.

Os métodos Actions do controlador que retornam um ActionResult podem passar um objeto model para a view. Isso permite ao controlador empacotar de forma limpa todas as informações necessárias para gerar uma resposta e, em seguida, passar essa informação para um template View que será usada para gerar a resposta HTML adequada.

Primeiro vamos criar algumas classes Model para representar Generos e Albuns em nossa loja. Vamos começar criando a classe Genero que representa os gêneros dos Álbuns.

Clique com o botão direito do mouse sobre a pasta Models e selecione a opção Add -> Class;

A seguir selecione o template Class e informe o nome Genero.vb e clique no botão Add;

A seguir digite o código abaixo neste arquivo:

Public Class Genero
    Public Property Nome() As String
        Get
            Return _nome
        End Get
        Set(value As String)
            _nome = value
        End Set
    End Property
    Private _nome As String
End Class
Public Class Genero

    Public Property Endereco As String

End Class

À esquerda eu estou usando a notação antiga para definir a propriedade Nome na classe Genero. A nova sintaxe usa o recurso das propriedades autoimplementadas e é mostrada à direita.

A seguir vamos criar a classe Album onde vamos definir as propriedades Titulo e Genero conforme o código abaixo:

Public Class Album

    Public Property Titulo As String
    Public Property Genero As String

End Class

Agora podemos modificar o controlador StoreController para usar a View que exibe informação dinâmica a partir do nosso modelo.

Vamos começar mudando a Action Detalhes da loja para que ela mostre as informações para um único álbum.

Adicione uma declaração Imports no topo da classe StoreControllers para incluir o namespace MvcMusicStore.Models; assim não vamos precisar digitar MvcMusicStore.Models.Album cada vez que quisermos usar a classe álbum. As demais declarações "Imports" da classe que vamos usar estão descritas a seguir:

Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.Mvc
Imports MvcMusicStore.Models

A seguir, vamos atualizar a Action Detalhes do Controlador StoreController para que ele retorne uma ActionResult em vez de uma string.

Após isso vamos modificar a lógica para retornar um objeto Album para a View. Mais adiante neste tutorial iremos recuperar os dados a partir de um banco de dados, mas por agora vamos utilizar "dados fictícios" para começar:


   
 ' GET: /Store/Detalhes
        Public Function Detalhes(id As Integer) As ActionResult
            Dim album = New Album() With
            { _
                 .Titulo = "Album "
& id _
            }
            Return View(album)
        End Function

A seguir vamos criar um template View que vai usar nosso Álbum para gerar uma resposta HTML.

Antes de fazer isso, precisamos dar um build no projeto para que a caixa de diálogo Add View saiba que a nossa classe Album recém-criada existe.

Você pode construir o projeto, selecionando o item do menu : Debug -> Build.

Agora clique com o botão direito do mouse no inteiror da Action Detalhes e selecione a opção Add View;

Vamos criar um novo template View a partir do controlador StoreController. que por padrão será gerada no arquivo \Views\Store\Detalhes.vbhtml.

Na janela Add View marque a opção “Create a strongly-typed” e na caixa de listagem selecione a nossa classe "Album"; com isso o diálogo irá criar o template View que espera que um objeto Album seja passado para ser usado:

Ao clicar no botão Add nossa view será criada no arquivo \Views\Store\Detalhes.vbhtml contendo o seguinte código:

@ModelType MvcMusicStore.Album

@Code
    ViewData("Title") = "Detalhes"
End Code

<h2>Detalhes</h2>

Note a primeira linha que indica que esta view é fortemente tipada para a nossa classe Album. O mecanismo de exibição Razor entende que foi passado um objeto Album, então podemos facilmente acessar as propriedades do modelo e ainda ter o benefício do IntelliSense no editor do Visual Studio.

Atualize a tag <h2> de forma que ela exiba a propriedade Titulo do álbum, modificando a linha para aparecer da seguinte forma:

@ModelType MvcMusicStore.Album

@Code
    ViewData("Title") = "Detalhes"
End Code

<h2>Album: @Model.Titulo</h2>

Observe que o IntelliSense é acionado quando você digita o ponto após a palavra-chave @Model, mostrando as propriedades e métodos que a classe Album suporta.

Vamos testar nossas alterações executando a aplicação e navegando para URL /Store/Detalhes/5 que deverá exibir os detalhes de um Album conforme a figura abaixo:

Agora vamos fazer a mesma atualização para o nosso método Action Procurar atualizando o método de forma que o mesmo retorne um ActionResult e modificando a sua lógica de forma que ele crie um novo objeto Genero e o retorne para um View.

Altere o método Action Detalhes do controlador StoreController conforme o código a seguir:

   ' GET: /Store/Procurar
        Public Function Procurar(genero As String) As ActionResult
            Dim generoModel = New Genero() With { _
                 .Nome = genero _
             }
            Return View(generoModel)
        End Function

A seguir clique com o botão direito do mouse no interior do método Detalhes e selecione Add View;

Na janela Add View marque a opção - Create a strongly-typed view - e selecione a classe Genero;

Após clicar no botão Add teremos o arquivo Procurar.vbhtml gerado na pasta \Views\Store.

Altere o seu conteúdo conforme abaixo:

Agora execute novamente o projeto e navegue para a URL /Store/Procurar?Genero=Disco para obter o seguinte resultado:

Finalmente, vamos fazer uma atualização um pouco mais complexa no método Action Index da loja e na view para exibir uma lista de todos os gêneros da nossa loja. Vamos fazer isso usando uma lista de gêneros com o nosso modelo de objetos, em vez de apenas um gênero único.

Altere o método Action Index no controlador StoreController conforme o código abaixo:

   Public Function Index() As ActionResult

        Dim generos = New List(Of Genero)() From { _
                New Genero() With { _
                     .Nome = "Disco" _
                }, _
                New Genero() With { _
                     .Nome = "Jazz" _
                }, _
                New Genero() With { _
                     .Nome = "Rock" _
                } _
            }
            Return View(generos)
        End Function

Clique com o botão direito do mouse no interior do método Action Index e selecione Add View;

A seguir marque a opção - Create strongly-typed view e selecione a classe Genero e clique no botão Add;

A view Index.vbhtml será criada na pasta /Views/Store:

Vamos alterar a declaração @model para indicar que a view estará esperando vários objetos do tipo Genero e não apenas um objeto. Altere a primeira linha de Views/Store/Index.vbhtml para:

@ModelType IEnumerable(Of MvcMusicStore.Genero)

Isto diz o mecanismo view Razor que estará trabalhando com um modelo de objeto que pode conter vários objetos Genero. Estamos usando um IEnumerable(Of Genero) em vez de um List(OF Genero), já que é mais genérico, o que nos permite mudar nosso tipo de modelo mais tarde para qualquer tipo de objeto que suporta a interface IEnumerable.

A seguir, vamos percorrer os objetos Genero no modelo, como mostrado no código abaixo:

Observe que temos suporte completo ao IntelliSense, de forma que quando digitamos "@Model". vemos todos os métodos e propriedades suportadas por um IEnumerable do tipo Genero.

Em seguida, o recurso de scaffolding examinou o objeto Genero e determinou que cada um tem uma propriedade Nome, de modo que os objetos são percorridos e exibidos. Ele também gera links Editar, Detalhes e Excluir para cada item individual. Vamos aproveitar isso mais tarde por vamos apenas ter uma lista de gêneros.

Ao executar o aplicativo e navegar até /Store, vemos que a contagem e a lista de gêneros exibida:

Adicionando links entre as páginas

Nossa URL /Store que lista os gêneros atualmente lista os nomes dos gêneros simplesmente como texto simples. Vamos mudar isso para que, em vez de texto simples tenhamos links para os gêneros vinculados a url /Store/Procurar apropriada de modo que ao clicar em um gênero musical como "Disco" o visitante irá navegar para a /Store/Procurar?genero=Disco.

Poderíamos atualizar nosso template \Views\Store\Index.vbhtml para a saída desses links usando o código como abaixo:

....
<ul>
    @For Each genero In Model
              @<li>
                  <a href="/Store/Procurar?genero=@genero.Nome">@genero.Nome</a>
               </li>
    Next
</ul> 

O código acima vai funcionar mas pode levar a problemas mais tarde, uma vez que se baseia em uma seqüência codificada. Por exemplo, se quisermos mudar o nome do controlador, precisaríamos pesquisar através de nosso código de procura de links que precisam ser atualizados.

Uma abordagem alternativa que podemos usar é tirar proveito de um método auxiliar HTML. A ASP.NET MVC inclui métodos de ajuda HTML que estão disponíveis a partir de nosso template de código View para executar uma variedade de tarefas comuns como esta.

O método auxiliar Html.ActionLink() é particularmente útil, e permite criar links HTML <a> de forma mais fácil cuidando também dos detalhes de como fazer caminhos URL corretamente codificados.

O Helper Html.ActionLink() tem várias sobrecargas diferentes para permitir especificar o máximo de informação que você precisa para o seus links. No caso mais simples, você vai fornecer apenas o texto do link e do método Action para ir quando o hiperlink é clicado no cliente. Por exemplo, podemos vincular o método /Store/Index() na página Detalhes da loja com o texto "Ir para a Loja" usando a seguinte chamada:

@Html.ActionLink("Ir para Loja", "Index")

Neste caso, não precisamos especificar o nome do controlador, porque estamos apenas ligando para outra Action no mesmo controlador que está renderizando a view atual.

Nossos links para a página Procurar precisarão passar um parâmetro, por isso vamos usar uma outra sobrecarga do método Html.ActionLink que possui três parâmetros:

Aplicando isso em nosso código vemos a seguir como escrever os links para a view Index da loja:

@ModelType IEnumerable(Of MvcMusicStore.Genero)

@Code
    ViewData("Title") = "Loja"
End Code

<h3>Procurando Generos</h3>
<p>
   Selecionado @Model.Count() generos:
</p>
<ul>
    @For Each genero In Model
              @<li>
                 @Html.ActionLink(genero.Nome,"Procurar", new with{ .genero = genero.Nome })                 
               </li>
    Next
</ul>

Agora, quando executarmos o nosso projeto novamente e acessar a URL /Store veremos uma lista de gêneros. Cada gênero é um hiperlink que quando clicado nos levará a URL /Store/Procurar?genero=[genero]:

 
 

O HTML para lista de generos vai ser gerado assim:

<ul>
    <li><a href="/Store/Procurar?genero=Disco">Disco</a>
</li>
    <li><a href="/Store/Procurar?genero=Jazz">Jazz</a>
</li>
    <li><a href="/Store/Procurar?genero=Rock">Rock</a>
</li>
</ul>

No próximo tutorial vamos definir o nosso Model e o acesso aos dados: ASP .NET - MVC Music Store - Model e Acesso aos Dados

Referências:


José Carlos Macoratti