.NET

3 jun, 2016

.NET – O padrão MVVM (Model-View-ViewModel) revisitado

Publicidade

Neste artigo vou apresentar os conceitos relacionados como padrão de projeto Model-View-ViewModel (MVVM).

O padrão MVVM é basedo no padrão MVC – Model-View-Controller e eles possuem em comum o Model-View e são separados pelo ViewModel.

O padrão MVVM foi projetado para implementar a vinculação de dados entre o seu ViewModel e a sua View conforme mostra a figura abaixo:

net_mvvm11

Estruturalmente, uma aplicação que usa o padrão MVVM consiste basicamente em três componentes principais: o Modelo, a Visão (View) e a ViewModel.

Essa tarefa (a vinculação de dados) pode ser feita usando o Knockout e atualmente com o AngularJS que faz isso muito bem usando o recurso do two-way databinding.

Nota: O AngularJS é considerado um framework Model-View-Whatever.(MVW) onde Whatever significa ‘Tudo oque funciona para você”.

A KnockoutJS é uma library JavaScript Open Source que usa o padrão MVVM (Model-View-ViewModel) para criar interfaces de usuário através de ligações declarativas de dados, interface de atualização automática, rastreamento de dependência e modelagem do lado cliente.

O conceito de MVVM deve ser muito intuitivo e familiar, especialmente para pessoas com experiência em WPF.

Você pode obter mais informações, bem como baixar a library JavaScript no site oficial do KnockoutJS.

O AngularJS é um framework JavaScript open-source, mantido pelo Google, que auxilia na execução de single-page applications. Seu objetivo é aumentar aplicativos que podem ser acessados por um navegador web, sob o padrão model–view–controller (MVC), em um esforço para facilitar tanto o desenvolvimento, quanto o teste dos aplicativos.

O AngularJS é construído sob a ideologia de que programação declarativa deve ser usada para construção de Interfaces de Usuário e componentes de software, enquanto que a programação imperativa é excelente para escrever as regras de negócio.

A coisa mais importante para se lembrar quando você for criar o seu ViewModel é que eles devem ser organizados de forma a tornar mais fácil a representação de como as suas Views vão usar os dados.

Esta é uma diferença importante pois os Models no padrão MVC são uma representação de como os dados estão armazenados no banco de dados.

net_mvvm13

No padrão MVC, o controlador de MVC aceita solicitações HTTP, obtém dados a partir do modelo, e encaminha o modelo para a view renderizando a saída. Já os ViewModels são a peça que realiza essa mesma tarefa, pois o ViewModel assume a responsabilidade de realizar, ou expor o comando que abriga toda a lógica de interface do usuário, além de buscar os dados e a ligação de dados.

Neste contexto, a ViewModel tem a responsabilidade de disponibilizar para a View uma lógica de representação.

Em uma aplicação ASP .NET MVC, podemos ter um ViewModel representando um conjunto de um ou mais Models e outras informações que desejamos exibir em uma View.

net_mvvm12

Na figura cima vemos que o ViewModel – AlteraSenha – é composto por informações do Model Usuario e de informações que precisamos exibir somente na View para confirmar a senha.

Uma maneira de saber se você precisa usar o padrão ViewModel é observar como foi definido o seu Model e verificar se nele existem definições que somente são utilizadas pelas Views e não têm nada a ver com seu modelo de domínio.

Se isso for encontrado no seu modelo, então talvez fosse o caso de você usar o ViewModel para separar essa responsabilidade a mais que está no seuModel.

Recursos usados: Visual Studio Community 2015.

Nota: Baixe e use a versão Community 2015 do VS. Ela é grátis e é equivalente a versão Professional.

Criando o projeto no VS Community

Abra o VS Community e clique em New Project. A seguir, selecione a linguagem Visual C# e o template ASP .NET Web Application, depois informe o nome Mvc_MVVM e clique no botão OK.

A seguir, selecione o template Empty, marque o folder MVC e clique no botão OK.

net_mvvm14

Definindo o Model

Vamos supor que temos uma classe Usuario no nosso modelo de domínio.

Vamos definir na pasta Models o nosso modelo de domínio representando pela classe Usuario que tem o seguinte código:

using System;
using System.ComponentModel.DataAnnotations;
namespace Mvc_MVVM.Models
{
    public class Usuario
    {
       public int Id { get; set; }
       [Required]
       public string Nome { get; set; }
       [Required]
       public string Email { get; set; }
       [Required]
       public string Senha { get; set; }
       public DateTime DataCriacao { get; set; }
    }
}

Esse é nosso modelo de negócio que representa um usuário com id, nome, e-mail, senha e data de inclusão no cadastro.

Definindo o ViewModel

Agora vamos supor que desejamos criar um formulário para alterar a senha do usuário e que vamos precisar exibir neste formulário as seguintes informações:

  • Nome

  • Senha

  • NovaSenha

  • ConfirmarSenha

Observe que temos informações que precisamos exibir que não constam no nosso modelo de domínio. Essas informações somente serão exibidas na view.

Com base nisso, vamos criar uma pasta ViewModel em nosso projeto e nesta pasta criar o nosso ViewModel representado pela classeUsuarioViewModel com o seguinte código:

using System.ComponentModel.DataAnnotations;
namespace Mvc_MVVM.ViewModel
{
    public class UsuarioViewModel
    {
        [Required]
        public string Nome { get; set; }
        [Required]
        public string Senha { get; set; }
        [Required]
        public string NovaSenha { get; set; }
        public string ConfirmarSenha { get; set; }
    }
}

Como você pode ver a nossa View Model contém as propriedades Nome e Senha do modelo de domínio e as propriedades NovaSenha e ConfirmarSenha que iremos somente exibir na view.

As propriedades Id, E-mail e DataInclusao não são necessárias em nosso ViewModel.

Definindo o Controlador

Vamos definir um controlador HomeController na pasta Controllers bem simples apenas para gerar a view para o nosso ViewModel.

O código do controlador HomeController é dado a seguir:

using Mvc_MVVM.ViewModel;
using System.Web.Mvc;
namespace Mvc_MVVM.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index()
        {
            UsuarioViewModel usuario = new UsuarioViewModel();
            return View(usuario);
        }
        [HttpPost]
        public ActionResult Index(UsuarioViewModel usuario)
        {
            //logica para alterar senha do usuároi
            return View();
        }
    }
}

Gerando a view Index para o método Action Index e usando as seguintes configurações:

net_mvvm16

Teremos o seguinte código:

@model Mvc_MVVM.ViewModel.UsuarioViewModel
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <script src="~/Scripts/jquery-1.10.2.min.js"></script>
    <script src="~/Scripts/jquery.validate.min.js"></script>
    <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
    @using (Html.BeginForm()) 
    {
        @Html.AntiForgeryToken()
        <div class="form-horizontal">
            <hr />
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            <div class="form-group">
                @Html.LabelFor(model => model.Nome, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.Nome, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.Nome, "", new { @class = "text-danger" })
                </div>
            </div>
           <div class="form-group">
                @Html.LabelFor(model => model.Senha, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.Senha, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.Senha, "", new { @class = "text-danger" })
                </div>
            </div>
            <div class="form-group">
                @Html.LabelFor(model => model.NovaSenha, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.NovaSenha, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.NovaSenha, "", new { @class = "text-danger" })
                </div>
            </div>
            <div class="form-group">
                @Html.LabelFor(model => model.ConfirmarSenha, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.ConfirmarSenha, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.ConfirmarSenha, "", new { @class = "text-danger" })
                </div>
            </div>
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Alterar Senha" class="btn btn-default" />
                </div>
            </div>
        </div>
       }
</body>

Temos uma view fortemente tipada onde o tipo é nosso ViewModel: UsuarioViewModel.

Executando o projeto teremos o resultado abaixo:

net_mvvm15

O ponto chave a lembrar aqui é que o ViewModel somente representa os dados que você deseja usar na sua view e nada mais (um ViewModel pode combinar dados de mais de um Model).

Creio que você entendeu o conceito básico do que é um ViewModel e quando usá-lo.

Pegue o projeto completo aqui: Mvc_MVVM.zip.