Hace poco pasé por la necesidad de enviar un formulario al servidor usando ajax en una aplicación MVC. El problema principal que tenía era que el formulario incluía un número “grande” de campos (o no tan grande pero resulta que soy un vago), por lo que no quería pasar el trabajo de tener que convertir todo eso a JSON como parámetros individuales. Tampoco deseaba, evidentemente, tener un método en el controlador al cual le llegaran 5 o más parámetros.
Pues bien… la vista en mi caso estaba diseñada para trabajar con un modelo definido por mí:
public class CustomerModel
{
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Language { get; set; }
public string ConfirmPassword { get; set; }
}
La vista:
<%@ Page Title=»» Language=»C#» MasterPageFile=»~/Views/Shared/Site.Master» Inherits=»System.Web.Mvc.ViewPage<CustomerModel>» %>
Mi formulario:
<% using (Html.BeginForm()) { %>
<div>
<fieldset>
<legend>Customer Information</legend>
<%: Html.LabelFor(m => m.FirstName) %>
</div>
<div class=»editor-field»>
<%: Html.TextBoxFor(m => m.FirstName) %>
</div>
<div class=»editor-label»>
<%: Html.LabelFor(m => m.LastName) %>
</div>
<div class=»editor-field»>
<%: Html.TextBoxFor(m => m.LastName) %>
</div>
<div class=»editor-label»>
<%: Html.LabelFor(m => m.UserName) %>
</div>
<div class=»editor-field»>
<%: Html.TextBoxFor(m => m.UserName) %>
</div>
<div class=»editor-label»>
<%: Html.LabelFor(m => m.Password) %>
</div>
<div class=»editor-field»>
<%: Html.PasswordFor(m => m.Password) %>
</div>
<div class=»editor-label»>
<%: Html.LabelFor(m => m.ConfirmPassword) %>
</div>
<div class=»editor-field»>
<%: Html.PasswordFor(m => m.ConfirmPassword) %>
</div>
<p>
<input type=»button» onclick=»uploadCustomerForm()» value=»Register» />
</p>
</fieldset>
</div>
<% } %>
La solución ideal para mí era poder pasar un objeto en javascript desde el cliente y que el controlador lo recibiera como el modelo asociado a la vista. Yo quería que el método en mi controlador se viera así:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveCustomer(CustomerModel model)
{
return Content(string.Format(«This is my content: {0}, {1}»,
model.LastName, model.FirstName));
}
Después de mucho andar por los rincones oscuros del mundo Google, hallé una solución limpia, elegante y simple.
Mi script:
<script src=»/Scripts/jquery-1.4.1.min.js» type=»text/javascript»></script>
<script type=»text/javascript»>
function uploadCustomerForm()
{
var data = $(‘form’).serialize();
//Enviar por post
$.post(‘<% =Url.Action(«SaveCustomer») %>‘, data, insertCallback);
}
function insertCallback(data)
{
alert(data);
}
</script>
Pues sí, así de simple resultó pasar el formulario al controlador y que este recibiera como parámetro el modelo asociado a la vista. Esta instrucción resume todo lo que necesitamos:
var data = $(‘form’).serialize();
Pero, ¿dónde está el truco?
Cada elemento input de mi formulario se crea usando el helper Html.TextBoxFor el cual es asociado a una propiedad del modelo. Esto, si miramos el HTML generado, crea un input cuyo ID Name corresponde con el nombre de la propiedad que, al ser serializado, crea un JSON que hace corresponder ID=valor una representación en el formato clásico de URL (param=valor&m2=valor2&…).
Formulario serializado:
Una curiosidad a tener en cuenta es: si miran el formulario podrán ver que he dejado fuera del formulario la propiedad Language de mi modelo, esto lo he hecho con toda intensión para que veamos que no es necesario que nuestro formulario se corresponda 100% con el modelo que tenemos asociado a la vista, pero, sí es necesario que cada ID Name de los elementos del formulario se corresponda con una propiedad llamada exactamente igual a la existente en el modelo.
Con esto… ya tenemos un post por ajax al controlador que recibe como parámetro el modelo asociado a la vista. Esto es posible también hacerlo usando ASP.NET, pero eso lo dejo para un próximo artículo.
😉
Buenas!
Un par de detalles (si me los permites) 😉
1. Serialize() NO crea una representación en JSON del formulario, crea una representación en el formato clásico de URL (param=valor¶m2=valor2&…). La representación en JSON del formulario que cuentas sería algo parecido a:
{«FirstName»:»aa»,»LastName»:»bb»,»UserName»:»aa.bb»,»Password»:»123″,»ConfirmPassword»:»123″}
De hecho, si usaras JSON, tu código NO funcionaría, a no ser que usaras MVC3 beta1. Las versiones anteriores de MVC no soportan, out-of-the-box, recibir datos en JSON, aunque por supuesto con un poco de código esto se soluciona 😉
2. El binding NO es por la propiedad id de los campos del formulario. Es por la propiedad name. Tanto si usas serialize() como si haces un submit clásico del form.
Un saludo! 😉
Gracias Eduard..
🙂 Arreglo el texto en un minuto. Lo del binding por el Name no se me ocurrió probarlo.. claro, como en mi caso el ID corresponde con el Name, de ahí el error.
Gracias por los comentarios..
Gracias!!!