IDataErrorInfo y ASP.NET MVC

Actualización con ASP.NET MVC 2 aquí

En la mayoría de las aplicaciones es probable que el usuario final necesite rellenar un formulario, modificar datos requeridos, etc. Como es normal, en muchas de estas ocasiones no se introducen los datos de forma correcta, obviamos alguno de los campos requeridos, etcétera.

La interfaz IDataErrorInfo nos ofrece la posibilidad de generar errores personalizados y poder mostrarlos en la interfaz de usuario correspondiente. Cuando creamos una vista de manera automática con MVC,  está preparada para mostrar estos errores gracias a los siguientes elementos: 

  • ValidationSummary: nos permite mostrar todos los errores producidos a modo de resumen en la vista.
  • ValidationMessage: puede resultar útil a la hora de mostrar cada error de forma particular, enlazando el mismo con un control de nuestra interfaz.
  • ModelState: se encargará de recopilar todos los errores producidos en nuestro objeto.

 Si generamos una vista de tipo Create o Edit, podemos ver ValidationSummary al comienzo y ValidationMessage en cada uno de los controles.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MovieManager.Models.Objects.Movie>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Edit
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
Edit</h2>
<%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm())
{%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="Name">
Name:</label>
<%= Html.TextBox("Name", Model.Name) %>
<%= Html.ValidationMessage("Name", "*") %>
</p>
<p>
<label for="Genre">
Genre:</label>
<%= Html.TextBox("Genre", Model.Genre) %>
<%= Html.ValidationMessage("Genre", "*") %>
</p>
<p>
<label for="Synopsis">
Synopsis:</label>
<%= Html.TextBox("Synopsis", Model.Synopsis) %>
<%= Html.ValidationMessage("Synopsis", "*") %>
</p>
<p>
<label for="Year">
Year:</label>
<%= Html.TextBox("Date", String.Format("{0:g}", Model.Date)) %>
<%= Html.ValidationMessage("Date", "*") %>
</p>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%=Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>

 Para mostrar un ejemplo, voy a implementar IDataErrorInfo en la clase Movie para controlar la creación y edición de películas.

using System;
using System.ComponentModel;

namespace MovieManager.Models.Objects
{
public class Movie : IDataErrorInfo
{
public int Id { get; set; }
public string Name { get; set; }
public string Genre { get; set; }
public string Synopsis { get; set; }
public DateTime? Date { get; set; }

public string this[string columnName]
{
get
{
var result = string.Empty;
switch (columnName)
{
case "Name":
{
if (string.IsNullOrEmpty(Name))
result = "Movie Name is required";
break;
}
case "Genre":
{
if (string.IsNullOrEmpty(Genre))
result = "Movie Genre is required";
break;
}
case "Year":
{
if (Date.HasValue)
{
if (Date.Value == DateTime.MinValue)
{
result = "Movie Date is must be real";
}
}
break;
}
}
return result;
}
}

public string Error
{
get { return string.Empty; }
}
}
}

Debemos crear dos propiedades:

  • Item: Devuelve el mensaje de error de la propiedad solicitada entre corchetes. En este caso, se están cubriendo aquellas propiedades que se consideran requeridas o, en el caso de Date, con una fecha correcta. Un string vacío se considera como acertado.
  • Error: Devuelve el mensaje de error por el cual el objeto es incorrecto. En esta ocasión, nos interesa más el caso particular de cada propiedad. Por ello, retornamos solamente un string.empty, ya que sería su valor por defecto.

Cuando llamamos a una acción desde la vista, y bindeamos la respuesta dentro de un objeto te tipo Movie, automáticamente comprueba que cada propiedad bindeada cumpla las condiciones implementadas en la propiedad ítem de nuestra clase. Si alguna de ellas no cumple las condiciones establecidas, quedará registrada en ModelState como una propiedad inválida.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Movie newMovie)
{
if (ModelState.IsValid)
{
_movieRepository.SaveMovie(newMovie);
return RedirectToAction("Index");
}
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Movie newMovie)
{
if (ModelState.IsValid)
{
_movieRepository.SaveMovie(newMovie);
return RedirectToAction("Index");
}
return View();
}

 Si intentamos crear una nueva película y no rellenamos los campos obligatorios obtendríamos la siguiente imagen:

¡Saludos!

2 comentarios sobre “IDataErrorInfo y ASP.NET MVC”

  1. Excelente! ahora lo que podes hacer es utilizar data annotations ahí o NHV 😉

    Si lo haces así genericamente, podes resolverlo en una clase base y te olvidas.

    Otra cosa que a mi me gusta de IDataErrorInfo es que funciona de esta manera «automática» con cualquier tecnología de binding en lo que es Presentación (wpf, winforms, asp.net, asp.net mvc, y silverlight).

  2. Hola José,

    Gracias por tu comentario =)

    Aunque en la versión oficial aún no soporta Data Annotations, ya sabemos que en la versión 2.0 viene ya listo y preparado 😉

    ¡Saludos!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *