Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 26 de abril de 2011
ASP.NET MVCSi a día de hoy hay algo fatalmente poco resuelto en ASP.NET MVC, es sin duda la localización. Aunque con un poco de paciencia se pueden poner en marcha sistemas completamente adaptados a distintos idiomas y culturas, la verdad es que se echa en falta una mayor consideración, entre otros, con los que osamos utilizar la coma para separar la parte entera de la decimal en un número.

Hace tiempo traté el tema por aquí, y aporté una solución para la versión 2 de ASP.NET MVC, que aún utilizaba las bibliotecas de scripting de Microsoft Ajax. Sin embargo, la versión 3 ha sustituido “de serie” esos componentes por jQuery Validate y el magnífico sistema de validaciones no intrusivas, por lo que todo lo dicho en aquella ocasión no vale ya para nada :-(

Validación decimal incorrectaEl problema radica en que el plugin jQuery Validate utiliza únicamente el punto como separador de decimales, por lo que la validación en cliente de valores de tipo decimal, float o double que utilicen la coma finalizará siempre en un error e impedirá el envío del formulario, como puede observarse en la captura de pantalla de la derecha.

Por cierto, antes de que se me olvide, hace unos meses reportaron este asunto como bug en Microsoft Connect. Si el tema os preocupa, podéis ir y votarlo a ver si conseguimos que este asunto se tenga en cuenta en próximas revisiones.

Sin embargo, estrictamente hablando, no se trata de un bug de ASP.NET MVC, puesto que la validación en cliente ha sido delegada por completo al plugin de jQuery, y éste es el que no tiene en cuenta los aspectos de internacionalización. Desde este punto de vista, quizás tendría más sentido, por tanto, esta issue reportada en Github sobre jQuery Validate, que propone su integración de forma nativa con jQuery Global.

Por tanto, me temo que se trata de un asunto de responsabilidad compartida (y dispersa, por tanto) entre los equipos de MVC, de jQuery Validate, y no sé si de alguno más. Esperemos que entre todos puedan solucionar de forma razonable el problema.

En cualquier caso, los que ya estamos creando aplicaciones con MVC 3 no podemos esperar las soluciones oficiales, que seguro llegarán más tarde o más temprano, y nos vemos obligados a buscar alternativas que nos permitan convivir con este problema de la forma más cómoda posible.

Y esto es lo que veremos en este post: varias posibilidades que tenemos para que la validación en cliente de valores decimales no nos compliquen demasiado la vida. Seguro que hay más, seguro que las hay mejores, pero ahí van unas cuantas opciones que nos pueden ayudar en escenarios como el descrito anteriormente.

1. Desactivar la validación en cliente

Está claro que el problema es en cliente, por lo que si desactivamos estas validaciones y dejamos que sea el servidor el que se encargue de comprobar que los valores de los distintos campos cumplen las restricciones impuestas por su tipo y las anotaciones de datos, ya no nos afectará más la absoluta indiferencia de jQuery Validate hacia las particularidades culturales.

Esto podemos conseguirlo de varias formas:
  • desactivar la validación en cliente de forma global, estableciendo a false la propiedad clientValidationEnabled en el web.config, lo cual dejará a toda la aplicación sin validaciones en cliente. Como solución es algo drástica, pero poderse se puede.
  • desactivar la validación en cliente de forma local, sólo en aquellos formularios en los que existan propiedades de tipo decimal, introduciendo el siguiente código Razor (o su correspondiente en ASPX) antes de la llamada a BeginForm():
    @{    
       Html.EnableClientValidation(false);
    }
    
  • desactivar la validación en cliente sólo en el campo que nos interese, que podemos conseguir introduciendo el siguiente script, imaginando que el campo decimal en el que queremos anular la validación en cliente tiene como identificador “Altura”:
    <script type="text/javascript">
       $("#Altura").removeAttr("data-val");
    </script>
Aunque podemos utilizar cualquiera de estas tres opciones,sin duda la menos violenta es la última, pues permite disfrutar de las validaciones en cliente y que sólo sean ignorados los campos conflictivos.

2. Modificar jQuery Validate

Esta es una solución algo bestia que he encontrado por ahí, pero soluciona el problema de un plumazo: modificar el código de jQuery Validate para que acepte comas en lugar de puntos para separar los dígitos decimales de los enteros tanto en la validación numérica como en los rangos.

En el blog de Lenard Gunda podéis encontrar de forma muy detallada los cambios a realizar al archivo jquery.validate.js (o a su versión minimizada). Hay, sin embargo, un par de detalles que debemos tener en cuenta si optamos por esta solución:
  • primero, que nos estamos separando de la distribución oficial del plugin. Si actualizamos la biblioteca jquery.validate, por ejemplo utilizando Nuget, volveremos a tenerlo todo como al principio, y tendremos que volver a introducir los cambios oportunos.
  • segundo, que esto no nos ayudará en aplicaciones adaptadas a varios idiomas; si modificamos el plugin para que acepte comas como separador, ya no volverá a aceptar el punto. Una solución rápida que se me ocurre para esto es tener dos versiones de la biblioteca (la original y la modificada), y referenciar desde la página la apropiada para la cultura actual.

3. Modificar la forma en que jQuery Validate parsea los decimales

Afortunadamente, el plugin de validación para jQuery es muy flexible, y permite introducir código personalizado para la validación de formato numérico y comprobación de rangos, lo que nos brinda la posibilidad de solucionar nuestro problema de forma muy limpia.

El siguiente código sería una primera aproximación a la solución del problema. Como podéis observar, simplemente introducimos en $.validator.methods.number y $.validator.methods.range las funciones que queremos utilizar para validar respectivamente los números y los rangos, reemplazando la coma por el punto antes de realizar la conversión con parseFloat():
<script type="text/javascript">
    $.validator.methods.number = function (value, element) {
        value = floatValue(value);
        return this.optional(element) || !isNaN(value);
    }
    $.validator.methods.range = function (value, element, param) {
        value = floatValue(value);
        return this.optional(element) || (value >= param[0] && value <= param[1]);
    }
 
    function floatValue(value) {
        return parseFloat(value.replace(",", "."));
    }  
</script>

Si incluimos este script en la página cuando la cultura activa sea la nuestra (o cualquier otra que también utilice la coma para separar decimales), tendremos el problema solucionado.

Una fórmula más elegante y universal sería modificar la función floatValue(), y en lugar de reemplazar de forma manual los caracteres, utilizar el plugin Global para realizar la conversión a flotante según la cultura actual. Los detalles de esto, sin embargo, los dejo para otro post.

En fin, que como habréis comprobado existen mil y un enfoques posibles para enfrentarnos al problema. Espero que las ideas que hemos ido comentando os sean de utilidad para implementar vuestras propias soluciones hasta que tengamos una vía “oficial” para conseguirlo.

Publicado en: Variable not found.

14 Comentarios:

sergio dijo...

Excelente! Son de esas soluciones que uno busca en Internet (a la numero 3 me refiero). Muchas gracias por tu dedicación

YoKeSe dijo...

Podriamos hacer esto?

@if((System.Threading.Thread.CurrentThread.CurrentUICulture.NumberFormat).CurrencyDecimalSeparator.Equals(","))
{
@:return parseFloat(value.replace(",", "."));
}

josé M. Aguilar dijo...

Ante todo, gracias a ambos por comentar :-)

@Yokese, pues sí, podría ser una buena alternativa para ajustar el parseo a la cultura del usuario. Gracias por el aporte! :-)

Saludos

Langdon dijo...

Fantástica solución!!! aunque realice la 'desactivar la validación en cliente sólo en el campo que nos interese' y si, funciono en firefox pero no en chrome, así que al final utilice la ultima. del uno al diez... un 20 :-p Gracias!!

Sebis dijo...

Me cuesta creer que a esta altura en el framework ASP.NET MVC tengamos que tener que hacer cosas como estas para poder trabajar con decimales :S (estará resuelto en ASP.NET MVC 4?)

Gracias por las distintas alternativas sobre como tratar el problema!

Danny Muñoz dijo...

Como alguien dijo por ahi.. Eso no es problema del framework...

Pero con un poco de habilidad todo se soluciona...

Xiogema dijo...

Hola, su solución fue muy buena para formularios Server, o sea, los que hacen el insertar y editar en una página nueva, pero para los formularios inline no me funcionó, tienes alguna idea de qué pueda hacer???? Muchas gracias de antemano

Santi dijo...

exelente aporte! se me ocurre que podría mejorarse si se pone el código del 3 en un js a parte y se incluye dentro del que tiene las validaciones de la siguiente manera :

bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*",
"~/Scripts/Common/ValidateFloatsComa*"));

espero les sirva abrazo!

Anónimo dijo...

Yo realice una modificación, para que funcione tanto con la coma como con el punto como separador decimal. Además al usar una expresión regular, si el separador es la coma e ingreso un punto me muestra como incorrecto el valor, con el código original me dejaba ingresar tanto el punto como la coma, sin mostrar el error en el lado del cliente. Espero le sirva a alguien.

$.validator.methods.number = function (value, element) {
if (getDecimalSeparator() == ",") {
return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\,\d+)?$/.test(value);
} else {
return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
}
}

function getDecimalSeparator() {
var n = 3 / 2;
n = n.toLocaleString().substring(1, 2);
return n;
}

José María Aguilar dijo...

Muchas gracias por la aportación!

Unknown dijo...

Oh por dios llevaba días buscando por esta contribución, muchísimas gracias!

He usado el método 3 combinando jquery con razor para crear algo muy dinamico para todas las culturas:

$(document).ready(function () {
$.validator.methods.number = function (value, element) {
value = floatValue(value);
return this.optional(element) || !isNaN(value);
}
$.validator.methods.range = function (value, element, param) {
value = floatValue(value);
return this.optional(element) || (value >= param[0] && value <= param[1]);
}
function floatValue(value) {
return parseFloat(value.replace("@caracterSeparadorGrupo", "").replace("@caracterSeparadorDecimal", "."));
}
PrepararMascarasDecimales("@caracterSeparadorDecimal","@caracterSeparadorGrupo");
});

function PrepararMascarasDecimales(currentDecimalSeparator, currentGroupSeparator) {
$('.money').mask("#" + currentGroupSeparator + "##0" + currentDecimalSeparator + "00", { reverse: true });
}

José María Aguilar dijo...

Genial, muchas gracias por la aportación! :)

Saludos!

Carlos H. dijo...

Saludos, estoy nuevo en el mundo MVC, actualmente estoy desarrollando un proyecto de prueba y quiero validar el . y la , en los campos decimal, double y float, de todo lo que ustedes han explicado aquí, como debo implementar los Jquery para que me funcione correctamente en el lado del cliente?, gracias y saludos...!!!

Anónimo dijo...

Increíble :Hoy día el problema persiste (ASP NET Core 6.0): No acepta la coma como separador decimal.
Use la alternativa 3, copiando el código en las vistas de Create y Edit, dentro de @section Scripts {. "Aqui lo copie" } y funcionó casi perfecto : acepta la coma como separador decimal y rechaza letras... Mas no rechaza el punto... Simplemente no lo considera dentro del número (10.45 =1045 pero si toma 10,45 = 10,45)
Felix Lopez
barbusanofelix@gmail.com