ASP.NET MVC Model Binding (parte 2 – outras formas de binding)

Blz, galera?

Continuando a série sobre ASP.NET MVC Model Binding iniciada <aqui>, neste post mostrarei outros dois tipos de binding suportados pelo default model binder: binding de arrays/collections e o binding de arquivos enviados (upload).

Vamos lá!

Binding de arrays e collections

O default model binder é esperto o suficiente para saber fazer o binding de dados vindos de vários campos com o mesmo nome.

Imagine que iremos postar informações de 3 campos de mesmo nome:

....
<input id="pessoa1" type="text" name="pessoas" />
<input id="pessoa2" type="text" name="pessoas" />
<input id="pessoa3" type="text" name="pessoas" />
....


Podemos, então, receber esses dados em uma action de várias formas. Como um IList<string>:

[HttpPost]
public ActionResult Salvar(IList<string> pessoas) { //... }

Ou como um List<string>:

[HttpPost]
public ActionResult Salvar(List<string> pessoas) { //... }

Ou ainda como string[]:

[HttpPost]
public ActionResult Salvar(string[] pessoas) { //... }

Simples e flexível, não é?

Podemos fazer o mesmo com tipos customizados, sem problemas:

@using MvcModelBinding.Models
@model IList<Contato>
<h2>Contatos</h2>
@using (Html.BeginForm("Salvar", "Contato"))
{
   for (var i = 0; i < Model.Count; i++)
   {
      @:Nome: @Html.TextBoxFor(m => m[i].NomeCompleto)
   }
   <input type="submit" value="salvar" />
}

No exemplo acima usamos o tipo “Contato” criado na parte 1 desta série para renderizar diversos inputs para o campo “NomeCompleto”.

O HTML gerado pelo helper associará um prefixo para cada nome do input, tal como abaixo:

<input type="text" name="[0].NomeCompleto" value="JOSE DA SILVA" />
<input type="text" name="[1].NomeCompleto" value="MARIA DA SILVA" />
<input type="text" name="[2].NomeCompleto" value="JOAO DOS SANTOS" />

(Estou assumindo que havia 3 contatos na lista, com os nomes completos acima.)

E então, na action, recebemos os dados como uma coleção de contatos:

[HttpPost]
public ActionResult Salvar(IList<Contato> x) { //... }

Pelo fato de passarmos uma coleção como parâmetro da action (IList<Contato>), o default model binder irá procurar por valores de cada campo do tipo “Contato”, que esteja prefixado por um índice, isto é, [0].NomeCompleto, [1].NomeCompleto e assim por diante.

Sendo assim, o nome da coleção passa a ser irrelevante (usei o nome de “x” para deixar isso bem explícito). Basta você garantir que, nos nomes dos inputs, estejam corretos os prefixos e os nomes das propriedades do objeto.

Ainda falando de binding de coleções, é possível nomear os prefixos ao invés de utilizar índices, por exemplo, [contato1].NomeCompleto ao invés de [0].NomeCompleto, além de fazer o binding para um Dictionary ao invés de List, mas para não alongar demais este post, deixarei essas duas particularidades para vocês pesquisarem.

Upload de arquivos

Para fazermos o binding de um arquivo (upload), basta recebermos um parâmetro do tipo HttpPostedFileBase em nossa action:

[HttpPost]
public ActionResult Upload(HttpPostedFileBase arquivo) { //... }

No entanto, para que seja enviado o arquivo em si, ao invés de meramente seu nome, precisamos definir no formulário o atributo enctype (isto é uma característica dos browsers e nada tem a ver com o ASP.Net MVC):


<form action="@Url.Action("Upload")" method="post" enctype="multipart/form-data">
<input type="file" name="arquivo" />
<input type="submit" />
</form>

Mais uma vez, não devemos esquecer da convenção de nome, portanto, o nome do parâmetro da action deve ser o mesmo nome dado ao input do tipo “file” (no exemplo, chamado de “arquivo”).

CONCLUSÃO

Nesta segunda parte da série sobre Model Binding, vimos mais dois recursos do default model binder, que é capaz de fazer corretamente o binding de arrays, coleções e arquivos.

Até agora, vimos que todo o processo é realizado automaticamente: recebemos os dados via parâmetro e o action invoker “se vira” para usar o model binder e “preencher” os parâmetros corretamente com os dados vindos da  requisição.

No próximo post, veremos como ativar manualmente o processo de model binding!

Até lá!

Boa semana a todos!

_________________________________________________________________

Este artigo faz parte da série:
1) ASP.NET MVC Model Binding (parte 1 – DefaultModelBinder)
2) ASP.NET MVC Model Binding (parte 2 – outras formas de binding)
3) ASP.NET MVC Model Binding (parte 3 – binding manual)
4) ASP.NET MVC Model Binding (parte 4 – custom binders)

6 comentários em “ASP.NET MVC Model Binding (parte 2 – outras formas de binding)

  1. Muito bom, Robson!! Quero testar receber uma lista dentro de outro objeto, será funciona normalmente ou tem alguma limitação?
    Abraço!

    1. Olá, Fernando
      Normalmente. De forma semelhante à mostrada para a lista de contatos. Supondo que Pessoa tenha uma lista de contatos:

      @model Pessoa
      
      .....dados da pessoa ....
      
       @for (var i = 0; i < Model.Contatos.Count; i++ )
          {
              <tr>
                  <td>
                      @Html.TextBoxFor(m => m.Contatos[i].Email)
                  </td>
                  <td>
                      @Html.TextBoxFor(m => m.Contatos[i].Fone)
                  </td>      
                   <td>
                      @Html.TextBoxFor(m => m.Contatos[i].NomeCompleto)
                  </td>  
              </tr>
          }
      ......
      

      []s!

  2. Valeu Robson! Mais um post de qualidade, parabéns e
    obrigado por compartilhar o seu conhecimento.

  3. Olá Robson, primeiramente quero agradecer pelos artigos de extrema qualidade! Muito obrigado por nos ajudar a crescer profissionalmente a cada dia, contribuindo conosco com seu enorme conhecimento 😉

    Tenho uma dúvida ao montar a View usando ModelBinder, espero que possa me ajudar, vamos lá:

    Tenho uma classe de Atendimento e outra de Interações do atendimento, ou seja, 1:N.

    Quero criar uma View tipada de Adicionar Atendimento, porém, como ainda não existe um atendimento, ou seja, o lado N das Interações está vazio, ao gerar o comando

    @Html.TextAreaFor(model => model.Interacoes[0].Descricao, new { rows = 5, @class = “form-control” })

    acontece o erro, sendo que as interações ainda não existem, mas queria criar de uma forma similar a essa para que o Asp.Net se encarregue de gerar o input para mim.

    Na View de alteração está tranquila, pois os dados já existem e só precisarão ser exibidos, o problema é ao Adicionar, pois o lado N (de interações) ainda não existe…

    Como posso fazer e qual a melhor forma de ser feita?

    Obrigado pela atenção.

Participe! Vamos trocar uma ideia sobre desenvolvimento de software!

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 )

Imagem do Twitter

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

Foto do Facebook

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

Foto do Google+

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

Conectando a %s