Sie sind auf Seite 1von 10

Qu es un Helper de MVC?

Como indica la palabra Helper, es algo que nos va a ayudar a definir una porcin de cdigo html dentro de las vistas que queremos utilizar en nuestra aplicacin MVC. Cuando estamos escribiendo el cdigo de nuestras vistas y pulsamos @ aparece el intellisenses de Visual Studio indicando los mtodos disponibles. Dentro de todos los mtodos existentes, vamos a definir tres grandes grupos: Html, Url y Ajax. HTML HELPERS La clase HtmlHelper nos va a permitir generar cdigo html en tiempo de ejecucin para definir controles web como: etiquetas, cajas de texto, combos, checkbox, vamos a encontrar helpers que nos permitan definir un formulario y la accin que se va a ejecutar en el momento de pulsar submit, o nos va a permitir por ejemplo definir enlaces. El uso de est clase te va a facilitar la escritura del cdigo de tu vista, evitando incluso cometer errores. Cmo se utiliza? Su eso es realmente sencillo, como comentaba anteriormente al pulsar @ sale el intellisense, y al teclear Html. vamos a ver todos los mtodos necesarios para realizar lo que necesitamos. Dentro de esta clase vamos a encontrar helpers que nos van a ayudar a definir los formularios de nuestra vista indicando la accin que se realizar al hacer submit, Para cada uno de los mtodos vamos a encontrar 2 definiciones, por ejemplo: Html.TextBox y Html.TextBoxFor. - La primera nos va a permitir generar nuestro elemento html, a travs de las caractersticas que le definamos como atributos, - el segundo adems nos va a permitir establecer el valor de nuestro input enlazndolo al modelo o propiedad del modelo que estemos utilizando dentro de nuestra vista lo que adems nos simplificar la tarea de envo de datos devueltos al servidor, ya que el dato ya estar en nuestro modelo. Ejemplos: @Html.TextBox("miCajaTexto", "Ejemplo", new Dictionary<string, object> { { @"Clase", "textbox" }) @Html.TextBoxFor(m=>m.MiPropiedad, new Dictionary<string, object> { { @"class", "textbox" }) Cmo definir un formulario? @using (Html.BeginForm("Accion", "Controlador", FormMethod.Post})) { Html.TextBoxFor(m=>m.Name) }

Dentro de los helpers incluidos en esta clase podemos encontrar dos de bastante utilidad: RenderAction y RenderPartial. Los cuales nos van a permitir renderizar dentro de nuestra vista, otra vista o vista parcial. RenderPartial: suele utilizarse cuando se comparten datos desde la vista que la define, por ejemplo: se quieren visualizar un conjunto de datos concretos del modelo. RenderAction: Suele utilizarse cuando una vista es independiente de la otra y los datos que se muestran en esta segunda vienen directamente desde el controlador. URL HELPERS La clase UrlHelper nos va a permitir generar urls para que las podamos utilizar dentro de nuestro aplicacin. El uso de esto nos va a evitar tener que escribir las urls del estilo ../../MiCarpeta/mipagina o ~/Content/images/miimagen1.png. Para ello disponemos de los siguientes mtodos: Url.Action: Nos va a devolver la ruta de una accin de un determinado controlador con los parametros que le indiquemos. Existen varias sobrecargas para dicha funcin por lo que se adapta a cualquiera de las opciones que necesitemos. De este mdo podriamos utilizar: <a href=@Url.Action(Accion,Controlador)>Ir a la Home</a> Url.Content nos va a convertir una ruta relativa en una ruta absoluta, por lo que nos va a evitar escribir una url incorrecta. Ejemplo: <link rel="stylesheet" href='@Url.Content("~/Content/screen.css")' type="text/css"/>

AJAX HELPERS Dentro del apartado de Html Helpers hemos hablado BeginForm por ejemplo que nos permita definir formularios para realizar la invocacin a una determinada accin. La clase AjaxHelper nos permite definir algunas caractersticas de nuestra vista pero ejecutndolas de forma asncrona a travs del uso de AJAX. @using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "MyHtmlElementId" })) { <input type="submit" value="OK" /> } Despus de presionar el boton submit de nuestro formulario se invocar de forma asincrona la accin del controlador, y cuando acabe mostrara en el elemento html cuyo ID sea MyHtmlElementId el valor devuelto por la accin.

VALIDACIN EN HTML HELPERS Cmo recibo en un controlador los datos mandados en un <form>? Esa es una pregunta tpica que se hace cualquiera que empieza con ASP.NET MVC. Si vienes de ASP antiguo (el clsico, el de Interdev) o de otra tecnologa como PHP, pues igual empiezas a buscar alguna propiedad llamada Form en la Request o algo as. No busques! No lo hagas porque la encontrars y entonces la usars y te perders uno de los elementos ms poderosos de ASP.NET MVC: el model binder. Para responder a esta pregunta vamos a usar esta vista: a) Sin usar helper. Solo etiquetado de HTML puro. <form method="POST"> Nombre: <input type="text" name="NOMBRE" /> <br /> Categora: <input type="text" name="CATEGORIA" /> <br /> <input type="submit" value="Enviar"/> </form> Todo HTML, que de moment no usamos Helpers Veamos cmo no acceder a los valores de un formulario. Si vienes de PHP o Interdev probablemente habrs llegado a teclear algo como esto: public ActionResult Index() { if (Request.Form["NOMBRE"] != null && Request.Form["CATEGORIA"] != null) { // Procesar alta return null; } else { return View(); } } Parece lgico no? Buscamos si los datos de formulario existen, y si existen es que venimos del POST y los procesamos. En caso contrario es que nos han hecho un GET por lo que devolvemos la vista. As no se hacen las cosas en ASP.NET MVC. Primero la lgica del GET (mostrar una vista) y del POST (procesar una alta) estn mezclados y eso no es buena seal. b) Separando Get y Post y usando la FormColecction

Por suerte ASP.NET MVC nos permite separar la lgica del GET de la del POST definiendo dos mtodos y decorando el que gestiona el POST con el atributo [HttpPost]. Tener la lgica separada sera un poco mejor y de hecho hay por Internet cdigo parecido al siguiente: public ActionResult Index() { return View(); } [HttpPost] public ActionResult Index(FormCollection form) { if (form["NOMBRE"] != null && form["CATEGORIA"] != null) { // Procesar alta } return null; } La clase FormCollection que aparece es una clase propia de ASP.NET MVC que tiene la misma informacin que Request.Form. Este cdigo funciona y de hecho vers cdigo as por Internet (se ve de todo en este mundo) pero la realidad es que hay muy pocas razones para usar FormCollection hoy en da. ASP.NET MVC tiene un concepto mucho ms poderoso, un amigo al que debes conocer y entender: el model binder. C) Aplicando el ModelBinder: parmetros en acciones con nombres de campo Si tu accin tiene un parmetro cuyo nombre es idntico al de un atributo name de un campo de un formulario, el model binder enlazar el valor del campo al parmetro automticamente. Es decir, el cdigo anterior nos puede quedar como: [HttpPost] public ActionResult Index(string NOMBRE, string CATEGORIA) { // procesar alta return null; } Esa es la regla bsica del model binder. Estoy seguro que esto te parece precioso pero a la vez es posible que una inquietud atormente tu nueva felicidad: si tengo un formulario con 20 campos se deben declarar 20 parmetros en la accin? D) Aplicando ModelBinder: parmetro con objeto Por suerte el equipo de ASP.NET MVC se dio cuenta de ello y as surge la segunda (y ltima que veremos en este post) regla del model binder. Si tu accin recibe como parmetro, un objeto de una clase que tiene una propiedad cuyo nombre es igual al de un atributo name de un

campo del formulario, el model binder enlazar automticamente el valor de este campo a la propiedad correspondiente del objeto recibido como parmetro. Quiz te parezca un poco rebuscado, pero lo que significa es que puedo tener una clase como la que sigue: public class CProducto { public string NOMBRE { get; set; } public string CATEGORIA { get; set; } } Y ahora en mi controlador recibir los datos simplemente usando un parmetro de tipo ProductoModel: [HttpPost] public ActionResult Index(CProducto PROD) { // procesar alta return null; } El model binder se encarga de crear el objeto PROD y de rellenarlo con los parmetros del formulario.

USO DE HELPERS PARA MANTENER EL ESTADO Pregunta: Vale, pero esto que tiene que ver con los helpers? De momento nada, pero ahora veamos que ocurre si no los ponemos. Imagina que alguien me da de alta un producto pero hace submit del formulario sin entrar el nombre que es obligatorio. Es decir, RELLENA la caja de CATEGORIA, PERO DEJA VACA LA DE NOMBRE Evidentemente yo tengo que mostrarle de nuevo la vista de dar de alta un producto CON LA CJA DE CATEGORA RELLENA CON LO QUE PUSO. Una forma no muy correcta de hacerlo es la siguiente: [HttpPost] public ActionResult Index(CProducto PROD) { if (string.IsNullOrEmpty(PROD.NOMBRE)) { return View(PROD); } // procesar alta return null; } Si el nombre del PROD est vaco devuelvo la vista como resultado, pero le paso el propio PROD. Para qu? Pues porque ahora tengo que meter cdigo para recuperar el estado de los controles. Es decir, debo rellenar el atributo value de los dos <input> con los valores que me vienen de PROD, ya que si no el usuario recibira una vista completamente vaca!. Si vienes

de Webforms este es un buen punto para empezar a odiar ASP.NET MVC, pero si lo sobrepasas luego, estoy seguro, lo empezars a amar irremediablemente. En resumen, para lograrlo debemos cambiar nuestra vista a algo as: @model MvcApplication1.Models.PROD @{ ViewBag.Title = "Index"; var NOMBREVALOR = Model != null ? Model.NOMBRE : null; var CATEGORIAVALOR = Model != null ? Model.PROD : null; }

<form method="POST"> Nombre: <input type="text" name="NOMBRE" value="@ NOMBREVALOR "/> <br /> Categora: <input type="text" name="VALOR" value="@CATEGORIAVALOR "/> <br /> <input type="submit" value="Enviar"/> </form> **Aqu me estoy aprovechando de una caracterstica de Razor que es que si igualamos un atributo de un elemento HTML a null, entonces Razor ni nos renderiza el atributo. Es decir si NOMBREVALOR o CATEGORIAVALOR vale null, el atributo value ni aparece en el HTML generado. Vale ahora imagnate un formulario con 20 campos y ya puedes empezar a llorar. Y esta es la primera razn por la que usar helpers. SI MODIFICAMOS LA VISTA PARA USAR LOS HELPERS: @model MvcvalidacionHelpers.Models.CPRODUCTO <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> <div> @using(Html.BeginForm("Index","Home")){ @:Nombre: @Html.TextBoxFor(m=>m.NOMBRE) <br /> @:Categora: @Html.TextBoxFor(m=>m.CATEGORIA) <br /> <input type="submit" value="Enviar"/>

} </div> </body> </html> Automticamente los controles del formulario recuperan su estado automticamente.

MOSTRAR ERRORES Y VALIDAR Pregunta: Y cmo muestro qu campo es el que ha producido el error? Hemos redirigido de nuevo al usuario porque se ha dejado de introducir el nombre del producto, pero estara bien que le informramos de que el nombre es obligatorio. Antes he dicho que el cdigo del controlador no era muy correcto. Y no lo es porque no usa un objeto de ASP.NET MVC llamado ModelState. El ModelState guarda entre otras cosas todos los errores que se producen al validar los datos por parte del controlador. As que lo suyo es introducir el error en el ModelState cuando lo detectamos: [HttpPost] public ActionResult Index(CProducto PROD) { if (string.IsNullOrEmpty(PROD.NOMBRE)) { ModelState.AddModelError("NOMBRE", "* Debe rellenar este campo"); return View(PROD); } // procesar alta return null; } El mtodo AddModelError aade un error a la propiedad indicada (el segundo parmetro es el mensaje de error). Ahora si ejecutamos el proyecto y envas el formulario dejando el campo nombre vaco, el cdigo HTML generado de vuelta ser algo como: <form method="POST"> Nombre: <input class="input-validation-error" id="NOMBRE" name="NOMBRE" type="text" value="" /> <br /> Categora: <input id="CATEGORIA" name="CATEGORIA" type="text" value="sdsd" /> <br /> <input type="submit" value="Enviar"/> </form> Fjate que el <input> generado para tener el nombre del producto ahora incluye una clase llamada input-validation-error. Esta clase la aade el helper cuando ve que hay un error asociado en el ModelState.

Luego ya por CSS es cosa tuya personalizar esta clase para que haga algo til (p. ej. mostrar el campo en rojo). Lo importante es que el helper la aade automticamente sin que tu tengas que hacer nada. <style type="text/css"> .input-validation-error { background-color:red} </style>

Si adems de poner en color rojo la caja de texto queremos poner un mensaje al lado, escribiremos en la accin del controlador: [HttpPost] public ActionResult Index(CPRODUCTO PROD) { if (string.IsNullOrEmpty(PROD.NOMBRE)) { ModelState.AddModelError("NOMBRE", "* Debe rellenar este campo"); return View(PROD); } // procesar alta return null; } En la vista mostraremos el mensaje siempre que el Mensaje de Validacin asociado a esa propiedad , devuelva un valor diferente de Nulo. - Si queremos adems que aparezca en color rojo el mensaje podemos aadir una etiqueta span con style. <div> @using(Html.BeginForm("Index","Home")){ @:Nombre: @Html.TextBoxFor(m=>m.NOMBRE) if (Html.ValidationMessageFor(x => x.NOMBRE) != null) { <span style='color:red'> @Html.Raw(Html.ValidationMessageFor(x => x.NOMBRE).ToString()) </span> } <br /> @:Categora: @Html.TextBoxFor(m => m.CATEGORIA) <br /> <input type="submit" value="Enviar"/> } </div> Pero como el mensaje devuelve una etiqueta span con su propia clase, podemos aadir un nuevo selector de clase en nuestra hoja de estilo y quitar la etiqueta span que hemos aadido nosotros a la vista.

La clase que incorpora el span automtico es: field-validation-error <form action="/" method="post"> Nombre: <input class="input-validation-error" id="NOMBRE" name="NOMBRE" type="text" value="" /> <span class="field-validation-error" data-valmsg-for="NOMBRE" data-valmsgreplace="true">* Debe rellenar este campo</span> <br /> Categora: <input id="CATEGORIA" name="CATEGORIA" type="text" value="dfasdfa" /> <br /> <input type="submit" value="Enviar"/> </form>

As que podemos poner en nuestra hoja de estilo algo as: <style type="text/css"> .input-validation-error { background-color:red;} .field-validation-error {color:red;} </style>

VALIDACIN MEDIANTE ANOTACION: Modelo: using System.Web.Mvc; using System.ComponentModel.DataAnnotations; namespace MvcvalidacionHelpers.Models { public class CPRODUCTO { [Required (ErrorMessage="El nombre es obligatorio")] public string NOMBRE { get; set; } [StringLength(8)] public string CATEGORIA { get; set; }

[Range (100,200)] public int PRECIO { get; set; } } } Controlador: public class HomeController : Controller { //

// GET: /Home/ public ActionResult Index() { return View(); }

[HttpPost] public ActionResult Index(CPRODUCTO PROD) { if (ModelState.IsValid == false) { return View(PROD); } // procesar alta return null; } }

Vista: <div> @using(Html.BeginForm("Index","Home")){ @:Nombre: @Html.TextBoxFor(m=>m.NOMBRE) @Html.ValidationMessageFor(x => x.NOMBRE) <br /> @:Categora: @Html.TextBoxFor(m => m.CATEGORIA) @Html.ValidationMessageFor(x => x.CATEGORIA) <br /> @:Precio: @Html.TextBoxFor(m => m.PRECIO) @Html.ValidationMessageFor(x => x.PRECIO) <input type="submit" value="Enviar"/> } </div>