Trabalhando com Partial View + multiplos Forms

Olá Pessoal, tudo bem ?

É comum surgir a necessidade de disponibilizar mais de um tipo de informação ou de realizar vários tipos de interações com o usuário em uma unica View.

Podemos citar como exemplo a necessidade de, em uma unica View, exibir uma tabela de clientes cadastrados e permitir o cadastro de clientes.

Outro exemplo seria ter a possibilidade do cliente realizar o login em um  site e também poder se cadastrar para receber newsletter do mesmo.

Seguindo esse cenário, vou demonstrar como implementar uma View que carrega três partialView’s, onde cada uma delas, terá uma responsabilidade diferente.

Vou  demonstrar como realizar o Request das informações dessas PartialView’s de forma assíncrona, utilizando o Ajax.BeginForm


Testes

Para pode guiar o design do nosso código e para validar cada método criado, utilizarei TDD


Começando: Cadastrando e  pesquisando funcionários

A View a ser criada terá como função:

  1. Cadastrar um novo cliente
  2. Realizar uma pesquisa sobre os clientes cadastrados

Vou iniciar criando uma um projeto MVC 5 utilizando o visual studio 2013.

figura-1

Screen Shot 09-16-14 at 12.43 AM

Fiz alguns ajustes na View para deixa-la limpa.

figura-2

Screen Shot 09-16-14 at 12.47 AM

criei os testes para inserir um novo cliente em uma coleção e para pesquisar:

using System;
using System.Collections.Generic;
using System.Linq;
using Demo.MultiplosForms.Domain;
using NBehave.Spec.NUnit;
using NUnit.Framework;

namespace Demo.MultiplosForms.Tests
{
    public class Dado_um_cliente : SpecBase
    {
        protected ClienteService clienteService;
        protected Cliente novoCliente;

        protected override void Establish_context()
        {
            base.Establish_context();
            clienteService = new ClienteService();
        }
    }

    public class Quando_for_cadastrar_um_cliente : Dado_um_cliente
    {
        protected override void Establish_context()
        {
            base.Establish_context();
            novoCliente = new Cliente()
            {
                Nome = "João",
                SobreNome = "Francisco",
                DataNascimento = DateTime.Parse("16/04/1991")
            };
        }

        protected override void Because_of()
        {
            base.Because_of();
            clienteService.Cadastrar(novoCliente);
        }

        [Test]
        public void Adicionar_o_cliente_na_listagem()
        {
            Cliente.ClientesCadastrados.ShouldContain(novoCliente);
        }

    }

    public class Quando_for_pesquisar_por_um_cliente_existente : Dado_um_cliente
    {
        private List<Cliente> clientesCadastrados;
        private IEnumerable<Cliente> _result;

        protected override void Establish_context()
        {
            base.Establish_context();
            clientesCadastrados = new List<Cliente>()
            {
                new Cliente{DataNascimento = DateTime.Parse("1/02/1985"),Id = new Guid(),Nome = "Alberto",SobreNome = "Silva"},
                new Cliente{DataNascimento = DateTime.Parse("1/02/1980"),Id = new Guid(),Nome = "Maria",SobreNome = "Ferreira"},
                new Cliente{DataNascimento = DateTime.Parse("1/02/1974"),Id = new Guid(),Nome = "Pedro",SobreNome = "De camargo"},
                new Cliente{DataNascimento = DateTime.Parse("1/02/1994"),Id = new Guid(),Nome = "Pedro",SobreNome = "Ferraz"},
            };

            Cliente.ClientesCadastrados = clientesCadastrados;
        }

        protected override void Because_of()
        {
            base.Because_of();
            _result = clienteService.Pesquisar("Pedro");
        }

        [Test]
        public void Retornar_cliente()
        {
            _result.ShouldNotBeNull();
            _result.Count().ShouldBeGreaterThanOrEqualTo(2);
        }
    }
}

Agora a implementação do método “Cadastrar” e “Pesquisar”

using System;
using System.Collections.Generic;
using System.Linq;

namespace Demo.MultiplosForms.Domain
{
    public class ClienteService : IClienteService
    {
        public void Cadastrar(Cliente novoCliente)
        {

            novoCliente.Id = Guid.NewGuid();
            Cliente.ClientesCadastrados.Add(novoCliente);

        }

        public IEnumerable<Cliente> Pesquisar(string nome)
        {
            return Cliente.ClientesCadastrados.Where(cliente => cliente.Nome.Contains(nome));
        }
    }
}

Definição da classe “Cliente”:

using System;
using System.Collections.Generic;

namespace Demo.MultiplosForms.Domain
{
    public class Cliente
    {
        public static List<Cliente> ClientesCadastrados { get; set; }
        public Guid Id { get; set; }
        public string Nome { get; set; }
        public string SobreNome { get; set; }
        public DateTime DataNascimento { get; set; }

        public Cliente()
        {
            if (ClientesCadastrados == null)
                ClientesCadastrados = new List<Cliente>();
        }

    }
}

Até este ponto, concluímos o desenvolvimento dos testes  para cadastro de cliente.

Agora, vamos desenvolver os testes para validar como a nossa Controller irá se comportar ao realizar a chamada dos métodos.

Abaixo, os testes para criar um novo cliente,para pesquisar por um cliente cadastrado e para validar o carregamento da nossa pagina inicial com todos os clientes já cadastrados:

using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Demo.MultiplosForms.Controllers;
using Demo.MultiplosForms.Domain;
using Moq;
using NBehave.Spec.NUnit;
using NUnit.Framework;
using System.Web;
using System.Net;
using System.Web.Routing;

namespace Demo.MultiplosForms.Tests.Controllers
{

    public class Dado_uma_HomeController : SpecBase
    {
        protected HomeController _controller;
        protected Cliente novoCliente;
        protected Mock<ControllerContext> ControllerContextMock;
        protected Mock<IClienteService> _ClienteServiceMock;

        protected override void Establish_context()
        {
            base.Establish_context();
            _controller = new HomeController();
            _ClienteServiceMock = new Mock<IClienteService>();
            ControllerContextMock = new Mock<ControllerContext>();
        }

    }

    public class Quando_for_realizar_uma_requisicao_para_cadastro_de_cliente : Dado_uma_HomeController
    {
        protected override void Establish_context()
        {
            base.Establish_context();
            novoCliente = new Cliente()
            {
                Nome = "João",
                SobreNome = "Francisco",
                DataNascimento = DateTime.Parse("16/04/1991")
            };

            _ClienteServiceMock.Setup(x => x.Cadastrar(novoCliente));
            ControllerContextMock.Setup(_ => _.HttpContext.Request["X-Requested-With"])
                .Returns("XMLHttpRequest");
        }

        protected override void Because_of()
        {
            base.Because_of();

            _controller.ControllerContext = ControllerContextMock.Object;
            _controller.Create(novoCliente);

        }

        [Test]
        public void Chamar_metodo_de_cadastro_do_ClienteService()
        {

            novoCliente.Id.ShouldNotBeNull();
        }
    }

    public class Quando_for_visualizar_a_Home : Dado_uma_HomeController
    {
        private ActionResult _actionResult;
        private ViewResult _ViewResult;

        protected override void Establish_context()
        {
            base.Establish_context();
            Cliente.ClientesCadastrados = new List<Cliente>(){
            (new Cliente { Nome = "Teste", SobreNome = "Test" })};
        }

        protected override void Because_of()
        {
            base.Because_of();
            _actionResult = _controller.Index();
        }

        [Test]
        public void Listar_todos_clientes_cadastrados()
        {

            _ViewResult = _actionResult as ViewResult;

            Extensions.ShouldBeInstanceOfType(_ViewResult.ViewBag.Clientes, typeof(IEnumerable<Cliente>));
            Extensions.ShouldNotBeNull(_ViewResult.ViewBag.Clientes);
        }

        public override void MainTeardown()
        {
            base.MainTeardown();
            Cliente.ClientesCadastrados.Clear();
        }
    }

    public class Quando_for_Pesquisar_um_cliente : Dado_uma_HomeController
    {
        private ActionResult _actionResult;
        private PartialViewResult _PartialViewResult;
        private string expected = "Pe";

        protected override void Establish_context()
        {
            base.Establish_context();

            _ClienteServiceMock.Setup(x => x.Pesquisar(expected)).Returns(new List<Cliente>());
            ControllerContextMock.Setup(_ => _.HttpContext.Request["X-Requested-With"])
                .Returns("XMLHttpRequest");

            Cliente.ClientesCadastrados = new List<Cliente> {
                new Cliente{DataNascimento = DateTime.Parse("1/02/1985"),Id = new Guid(),Nome = "Alberto",SobreNome = "Silva"},
                new Cliente{DataNascimento = DateTime.Parse("1/02/1980"),Id = new Guid(),Nome = "Maria",SobreNome = "Ferreira"},
                new Cliente{DataNascimento = DateTime.Parse("1/02/1974"),Id = new Guid(),Nome = "Pedro",SobreNome = "De camargo"},
                new Cliente{DataNascimento = DateTime.Parse("1/02/1994"),Id = new Guid(),Nome = "Pedro",SobreNome = "Ferraz"},
            };
        }

        protected override void Because_of()
        {
            base.Because_of();
            _controller.ControllerContext = ControllerContextMock.Object;
            _actionResult = _controller.Pesquisar(expected);
        }

        [Test]
        public void Listar_clientes_encontrados()
        {

            _PartialViewResult = _actionResult as PartialViewResult;

            Extensions.ShouldBeInstanceOfType(_PartialViewResult.Model, typeof(IEnumerable<Cliente>));
            Extensions.ShouldNotBeNull(_PartialViewResult.Model);
        }

    }
}

 

Antes de iniciar a implementação dos métodos na classe Controller, vou utilizar o Framework Ninject para realizar a injeção de dependência.

Criei uma nova class library no projeto

figura-3

Screen Shot 09-19-14 at 12.03 AM

 

Adicionei o Ninject utilizando o Nuget

figura-4

Screen Shot 09-19-14 at 12.04 AM

 

Após a instalação do Ninject, eu criei uma classe chamada NinjectModules. Esta classe de configuração tem como função, falar para o Ninject qual classe concreta que ele deverá instanciar, isto é, quando eu utilizar a classe IClienteService, ele deverá criar uma instancia da classe mapeada nesse arquivo de configuração, que no nosso caso é a classe ClienteService.

using Demo.MultiplosForms.Domain;
using Ninject.Modules;
namespace Demo.MultiplosForms.IoC
{
    public class NinjectModules:NinjectModule
    {
        public override void Load()
        {
            Bind<IClienteService>().To<ClienteService>();
        }
    }
}

Com a classe de configuração concluída, vamos então para a implementação da Controller.

Reparem que criei uma propriedade do tipo IKernel. Está propriedade recebe uma instancia da classe de configuração que criamos.

Agora, toda chamada aos métodos das classes mapeadas na classe NinjectModules é realizada através desta propriedade. Podemos ver como isto é feito na Action Create.


using System.Web.Mvc;
using Demo.MultiplosForms.Domain;
using Demo.MultiplosForms.IoC;
using Ninject;

namespace Demo.MultiplosForms.Controllers
{

    public class HomeController : Controller
    {
        private IKernel kernel;

        public HomeController()
        {
            kernel = new StandardKernel(new NinjectModules());
        }

        public ActionResult Index()
        {
            ViewBag.Clientes = Cliente.ClientesCadastrados;
            return View();
        }

        public ActionResult Create(Cliente novoCliente)
        {
            if (Request.IsAjaxRequest())
            {
                kernel.Get<IClienteService>().Cadastrar(novoCliente);
                return PartialView("_Clientes", Cliente.ClientesCadastrados);
            }
            else
            {
                return RedirectToAction("Index");
            }
        }

        public ActionResult Pesquisar(string uiTxtNome)
        {
            if (Request.IsAjaxRequest())
            {
                return PartialView("_Clientes", kernel.Get<IClienteService>().Pesquisar(uiTxtNome));
            }
            else
            {
                return RedirectToAction("Index");
            }
        }
    }
}

Com esta etapa concluída, iniciamos agora o desenvolvimento das nossas Views.

Views

Como escolhemos o template MVC no inicio da criação do nosso projeto, e já deixamos a nossa View “Home” limpa, vamos precisar criar somente as três partialView’s.

Vamos começar primeiro pela partialView que irá cadastrar novos clientes.

Clique com o botao direito na pasta “Shared” que fica dentro da pasta “Views”, navegue até o menu “Add” e escolha a opção “view…”

figura-5

Screen Shot 09-18-14 at 11.41 PM

 

Na figura-6, podemos observar que:

No campo “View name”, defini que o nome da minha viu será _”CadastroDeClientes.” Por convenção, colocamos underline(_) como prefixo ao criar um nome para uma partialView.

No campo “Template” escolhi a opção create, pois desta forma, o próprio Visual Studio fará a maior parte do trabalho ao criar o Hpara o input dos dados.

No campo Model class, escolhi a entidade que ele irá utilizar para tipar a minha partialView.

Para terminar, escolhi a opção “Create as partial view”, pois é ela que irá dizer que a View a ser criada é realmente uma PartialView.

 

figura-6:

Screen Shot 09-18-14 at 11.43 PM

 

 

Com a partialView “_CadastroDeCliente” criada, vamos realizar a chamada dela na View:

@{
    ViewBag.Title = "Home Page";
}

<div class="row">
    @Html.Partial("_CadastroDeCliente")

</div>

Ao executar a aplicação, você poderá ver a PartialView carregada

figura-7
Screen Shot 09-29-14 at 10.20 PM

Se você clicar no botão “Create”, irá perceber que mesmo utilizando o Ajax.BeginForm, o POST é realizado e toda a pagina é  carregada. Para resolver este problema, na pagina “_Layout”, adicione o script “unobtrusive” que fica

no diretório “jqueryval” criado pelo Bundle.

 

figura-8

Screen Shot 09-29-14 at 10.30 PM

Esse ScriptBundle irá inserir a referencia de todo arquivo que esteja na pasta script e que seu nome comece  com “jquery.validade“. Entre estes arquivos esta o unobtrusive.

 

chamando o bundle na nossa View:

figura-9

Screen Shot 09-29-14 at 10.37 PM

Para completar, Adicione também, o script “jquery.unobtrusive“. Para adiciona-lo utilize o Nuget.

figura-10

Screen Shot 10-01-14 at 12.23 AM

adicione o arquivo no bundle agora:

Screen Shot 10-01-14 at 12.25 AM

execute novamente a aplicação e ira perceber que o post agora é realizado de forma assíncrona.

Agora que terminamos de referenciar os scripts necessários para fazer os nossos posts de forma assíncrona, vamos criar uma segunda partial view. Esta partial view será responsável por realizar a pesquisa dos clientes cadastrados.

Screen Shot 10-01-14 at 12.31 AM

adicionei o seguinte código html para realizar a pesquisa:

@using (Ajax.BeginForm("Pesquisar", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "dvResultado" }))
{
    <div class="row">
        <div class="col-md-2">
            <input type="text" name="uiTxtNome" class="form-control" />
        </div>
        <div class="col-md-2">
            <input type="submit" value="Pesquisar" class="btn btn-primary">
        </div>
    </div>
}

<div class="clearfix"> </div>

Agora, para finalizar, vamos criar a nossa partial view que irá exibir em uma tabela, todos os clientes cadastrados:
Screen Shot 10-01-14 at 12.42 AM

o código Html da partial view “_Clientes”:

@model IEnumerable<Demo.MultiplosForms.Domain.Cliente>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Nome)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.SobreNome)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.DataNascimento)
        </th>
    </tr>
    @if (Model != null)
    {
        foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Nome)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.SobreNome)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.DataNascimento)
                </td>
            </tr>
        }
    }
    else
    {
        <tr>
            <td colspan="4">Nenhum cliente cadastrado</td>
        </tr>
    }

</table>

Agora a nossa View Index chamando as partial view:

@{
    ViewBag.Title = "Home Page";
}

<div class="row">
    @Html.Partial("_CadastroDeCliente")
    @Html.Partial("_PesquisarCliente")

    <div id="dvResultado">
        @Html.Partial("_Clientes",ViewBag.Clientes as IEnumerable<Demo.MultiplosForms.Domain.Cliente>)
    </div>

</div>

Github

Anúncios

2 comentários sobre “Trabalhando com Partial View + multiplos Forms

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

w

Conectando a %s