viernes, 24 de agosto de 2007

RequiredFielValidator, Buttons y ValidationGroup

Esta mañana me he encontrado con un pequeño obstáculo en la programación de mi proyecto. Como considero que es interesante que la gente lo sepa, pues aquí os lo dejo, por si alguna vez os hace falta.
Primero os pongo en situación...

En un control de usuario, necesitaba insertar unos controles de validación de ASP.NET. Concretamente, necesitaba el control RequiredFieldValidator. Como supongo que sabéis, este control evalúa el valor de un control de entrada en una aplicación ASP.NET para asegurarnos que tiene algún valor. De esta forma evitamos errores en la introducción de valores.
Este control forma parte de un grupo de controles de validación que nos permiten no sólo evitar que un control de entrada se deje vacío, sino además podemos hacer que sigan ciertas normas.

El caso es que en el control había diferentes popups cada uno con su texto de entrada y sus botones asociados para introducir los datos o cancelar la introducción. La verdad es que es un tema un poco difícil de explicar, así que propondré un escenario parecido pero más sencillo.

Imaginad que estamos programando una web en la que, entre otras cosas, tenemos que poder introducir nuevos productos que luego podremos vender. Asimismo, tenemos que poder introducir algún tipo de clasificación. Concretamente, tenemos una página de la web en la que podemos crear nuevos tipos y subtipos de productos. Simplificando al máximo, el código sería el siguiente:

<h3>Nuevo Tipo</h3>

<asp:label id="tipoLabel" runat="server" text="Introducir nuevo tipo:"></asp:label>
<asp:textbox id="tipoTextBox" runat="server"></asp:textbox>

<asp:button id="introducirTipoButton" runat="server" text="Introducir" >
</asp:button>

<h3>Nuevo Subtipo</h3>

<asp:label id="subtipoLabel" runat="server" text="Introducir nuevo subtipo:"></asp:label>
<asp:textbox id="subtipoTextBox" runat="server"></asp:textbox>
<asp:button id="introducirSubtipoButton" runat="server" text="Introducir" validationgroup="subtipoValidation">
</asp:button>


A este código le añadimos dos controles de validación del tipo RequiredFielValidator para evitar que un usuario "despistado" pueda darle a cualquiera de los botones e introducir una clase o subclase vacías. El código resultante será:
<h3>Nuevo Tipo</h3>

<asp:label id="tipoLabel" runat="server" text="Introducir nuevo tipo:"></asp:label>
<asp:textbox id="tipoTextBox" runat="server"></asp:textbox>
<asp:RequiredFieldValidator ID="tipoRequiredFieldValidator"
runat="server"
ErrorMessage="Escribe algo!!"
ControlToValidate="tipoTextBox"/
<
<asp:button id="introducirTipoButton" runat="server" text="Introducir" validationgroup="tipoValidation">
</asp:button>

<h3>Nuevo Subtipo</h3>

<asp:label id="subtipoLabel" runat="server" text="Introducir nuevo subtipo:"></asp:label>
<asp:textbox id="subtipoTextBox" runat="server"></asp:textbox>
<asp:button id="introducirSubtipoButton" runat="server" text="Introducir" >
</asp:button>
<asp:RequiredFieldValidator ID="subtipoRequiredFieldValidator"
runat="server"
ErrorMessage="Aquí también tienes que escribir algo!!"
ControlToValidate="subtipoTextBox" /
>

Parece que todo está correcto, ¿verdad? Pues bien, probadlo y veréis como no es así.
El problema reside en que los controles de validación están esperando una señal para comprobar si el usuario ha rellenado o no el textbox correspondiente. Como los dos botones causan validación (es el comportamiento por defecto) en cuanto apretamos uno de ellos, pues los controles de validación comprueban si hemos escrito algo en los controles que ellos controlan, valga la redundancia, de forma que apretemos el que apretemos siempre saldrán los mensajes de comprobación.
La solución que se creó para esto es bien sencilla. Podemos asociar los controles (el Button y el RequiredFielValidator) a un grupo de validación mediante la propiedad ValidationGroup. De esta forma, el Button sólo enviará la señal a aquellos controles que formen parte del mismo grupo de validación.
El código quedaría así:
<h3>Nuevo Tipo</h3>

<asp:label id="tipoLabel" runat="server" text="Introducir nuevo tipo:"></asp:label>
<asp:textbox id="tipoTextBox" runat="server"></asp:textbox>
<asp:RequiredFieldValidator ID="tipoRequiredFieldValidator"
runat="server"
ErrorMessage="Escribe algo!!"
ControlToValidate="tipoTextBox"
ValidationGroup="tipoValidation" /
<
<asp:button id="introducirTipoButton" runat="server" text="Introducir" validationgroup="tipoValidation">
</asp:button>

<h3>Nuevo Subtipo</h3>

<asp:label id="subtipoLabel" runat="server" text="Introducir nuevo subtipo:"></asp:label>
<asp:textbox id="subtipoTextBox" runat="server"></asp:textbox>
<asp:button id="introducirSubtipoButton" runat="server" text="Introducir" validationgroup="subtipoValidation">
</asp:button>
<asp:RequiredFieldValidator ID="subtipoRequiredFieldValidator"
runat="server"
ErrorMessage="Aquí también tienes que escribir algo!!"
ControlToValidate="subtipoTextBox"
ValidationGroup="subtipoValidation" /
>
Sencillo, ¿verdad? Una vez se conoce la solución, sí que es sencillo. Por si os sirve de algo, la solución la encontre en este post de los foros de ASP.NET.

¿Le ponemos la guinda al pastel?
Imaginad que todo esto lo tengáis en un popup y queráis poner un botón que cancele la introducción y cierre el popup. Independientemente del código asociado, al introducir un botón, cuando hagamos click en él, se producirá la validación de nuevo y, si tenemos algún control de validación sin grupo de validación asociado, puede que haga que dicho control salte porque no se cumple la condición que el valida. Por supuesto, no se cerrará el popup...
¿Y esto por qué pasa? Sencillamente, porque, como hemos dicho, los controles Button por defecto tienen una propiedad activada que se llama CausesValidation (causa validación).
Lo único que tenemos que hacer es poner dicha propiedad a false... (OJO, siempre que no necesitemos dicha validación!!).
Por ejemplo, el botón quedaría así:
<asp:Button ID="cancelButton"
runat="server"
Text="Cancelar"
CausesValidation="false" /
>

Esta solución la tuve que aplicar en un PopupControlExtender del Ajax Control Tookit de ASP.NET para cerrar el popup que se abre y que no me causara la validación de los datos introducidos. El post dónde encontré la solución lo podéis leer aquí.
Espero que os pueda servir de algo.

Un saludo!