Archivo de la categoría: Javascript

Clases en Javascript: The right way

Como bien me apuntan por ahí ( y sin acusar a nadie! jajaja @o0Lillo0o), a la hora de crear clases en javascript no es bueno ni eficiente definir funciones en el propio objeto, sino que lo suyo es hacerlo en el prototype.

Aquí veremos como se ha de crear una clase en Javascript bien hecho (al menos con mi nivel :-p). Crearemos las clases como funciones.

Por ejemplo, creemos una clase Coche. Para ello definiremos que el constructor recibe el nombre del coche.

function Coche( nombre ){
	this.nombre = nombre;
}

Coche.prototype.setNombre = function (nombre){
	this.nombre = nombre;
}

Coche.prototype.getNombre = function (){
	console.log(this.nombre);
}

var c = new Coche("ford");
c.getNombre(); //ford
c.setNombre("Angel del infierno");
c.getNombre(); //adivina!

Y así para cualquier funcion de clase que necesites, se la añades al prototype….

 

Espero que esta opción sea más purista 😉

Singleton en Javascript

Toda persona que programa se ha topado alguna vez con los patrones de diseño, algo que yo creo que, cuando los aprendes (en mi caso en la Universidad se ven algunos y los aplicamos en Java) son un truño algo aburrido ya que todo sacado de contexto lo es.

Una vez que empiezas a meter el morro en el mundo de la programación y no eres no solamente el único que programa (es típico que haya grupos de 2 o 3 como máximo en España a la hora de hacer la mayoría de las prácticas) sino que la aplicación la usa mucha, mucha gente a la vez, te das cuenta de que los patrones de diseño no solamente son necesarios y útiles, sino que además… molan.

Aquí quiero ir poniendo algún que otro patroncillo y la intención es implementarlos en Javascript 🙂

Uno de los patrones más sencillos y que nos sirve para empezar, es el “Singleton”. Este patrón nos sirve para tener una única instancia de una clase, de manera que cualquiera que acceda a esa clase, trabaje sobre la misma instancia.

Esto, para una clase “Usuario” no vale para nada, ya que cuando creo un usuario y lo manipulo, quiero que sea nuevo y único… pero por ejemplo para manejar un objeto de conexión a una base de datos, sí que nos interesa que se acceda a uno solo, de manera que no tengamos 40.000 conexiones abiertas con la BBDD sino solamente una.

Para eso, nos tenemos que asegurar que nadie puede instanciar objetos de la clase ( La instancia se devuelve típicamente con un “getInstance()” )

var ClaseSingleton = (function () {

  // instancia que usaran los demas
  var instance;

  function init() {

    // Singleton

    // metodos que no podran ser accedidos por
    // nadie que use la instancia

    function _metodoPrivado(){
        console.log( "No me ves" );
    }

    var _variableProvada = "Soy inalcanzable para ti";

    var _numAleatorio = Math.random();

	//Esto es lo que devuelve la funcion init(), privada dentro de
	//la clase singleton. Lo que devuelve son, en definitiva
	//las funciones que hacemos publicas

    return {

      // Cosas publicas
      metodoPublico: function () {
        console.log( "Mirameee!" );
      },

      propiedadPublica: "Soy de todos ;)",

      getNumAleatorio: function() {
        return _numAleatorio;
      }

    };

  }; //fin init()

	//esto es a lo que obtendra acceso cualquiera que instancie el singleton
	//solo le damos opcion de hacer el getInstance()
  return {

    // Nos da la instancia si existe
    // o crea una nueva si no existe
    getInstance: function () {

      if ( !instance ) {
	    //el objeto tendra los metodos y props que devuelve init en el return!!
        instance = init();
      }

      return instance;
    }

  };

})();

¿Qué está pasando?

La “clase estática” ( Para simular la clase estática, hacemos una función que se ejecuta en cuanto se define, de manera que no es un objeto instanciable en javascript mediante un “new”, como sería una “clase” normal. ) “ClaseSingleton” tiene, digamos, dos partes: parte privada y la parte pública.

Para hacer una parte privada, es decir, inalcanzable desde fuera de la clase, escribirmos código dentro de la función que simula la clase con normalidad, esto es, creamos variables, funciones… lo que sea necesario, pero para que podamos controlar lo que se hace público y cómo, usaremos la sentencia “return”, devolviendo un objeto javascript que contendrá lo que queramos hacer público.

Es decir, ClaseSingleton puede tener 187 funciones pero si en el return solamente devolvemos una función, esa será la única pública.

Es decir, lo público irá en el objeto del return.

Lo que se hace aquí es dar solamente acceso a aquellos que usan nuestra clase a una función llamada getInstance que devuelve la instancia o la crea en caso de no existir. Cuando la crea, lo hace a través de la función “init” (el nombre es arbitrario, podrias llamarlo de otra manera…). Al llamar a la función init() lo que hace es obtener lo que esta funcion le devuelve en su return, que es un objeto javascript que contiene funciones y variables, que son en realidad, las acciones que ofrece nuestra clase.

 

Más información y MUY buena sobre patrones de diseño en javascript aquí.

 

Clases en Javascript

En Javascript no existen las clases como las conocemos en Java o en C# o en PHP… pero se pueden simular de forma muy chula mediante una función. Para ello, tenemos que seguir esta estructura y listo


function nombreClase(){
	//atributos
	var a1 = "hola!";
	var _a2;

	//funciones
	function _f1(){};
	function f2(){ console.log("ejecuto f2"); };

	//return con la interfaz que ofrecemos
	return{
	    varPublica: a1,
	    funcionDeLaClase: f2
	}

};

Aquí no hay mucho que explicar, solamente que si instanciamos un objeto e la clase, solamente tendremos acceso a “varPublica” y “funcionDeLaClase”, tal y como hemos definido…

Aquí meto unos pantallazos para que veais como se puede probar en la consola de chrome 🙂

Se puede ver que varPublica y funcionDeLaClase estan disponibles

Se puede ver que varPublica y funcionDeLaClase estan disponibles

 

ejemplo de ejecución:

acciones clase

Venga, juega un poco y define tus propias clases!

 

Cómo crear un validador de campos de formulario con JQuery

En cualquier proyecto web lo más normal es que tengamos que validar los datos que el usuario nos introduce en los formularios. Aunque esto hay que hacerlo tanto desde el lado del servidor como del lado del cliente, este post va sobre el lado del cliente y está pensado para gente que empieza en esto. Vamos a ver cómo hacer un validador de datos con jQuery.

Aunque hay librerías que hacen esto por nosotros, no siempre queremos podemos utilizarlas por lo que sea (tu jefe no te deja… por ejemplo) y nos vemos validando los datos en cada una de las páginas utilizando un montonazo de Javascript que además siempre es parecido. Esto no solamente gasta mucho tiempo sino que el día que descubramos un error, tendremos que cambiarlos en todas las páginas.

Si hubiéramos hecho esto bien y hubiéramos tenido un fichero javascript solo para estas funciones, tal vez el esfuerzo habría sido menor… pero bueno, no siempre es así y además a veces lo que queremos es hacer algo que nos parezca “chulo” (aunque en realidad no lo sea…).

Pues bien, vamos a ver las bases para crear un validador de entrada de usuarios con jQuery. Vamos a aceptar solamente un pequeño tipo de datos/entradas que tendrás que ir ampliando poco a poco y bueno, si ves problemas en el código, es bienvenido que lo digas.

Lo que haremos es, una vez tengamos nuestro validador, pasarle un JSON con los campos que queremos validar, su tipo…y opciones que acepte nuestro validades y éste nos devolverá un objeto que indicará si hay éxito o no.

La entrada será algo como esto por cada campo:


{
	id: 		"#lugarConcierto",
	msg: 		"Pon un lugar/recinto",
	tipo: 		"text",
	mandatory: 	true
},

El id nos servirá para identificar el campo
El msg para poner un mensaje que mostrar en caso de error
El tipo nos dirá qué tipo de validación queremos llevar a cabo (de las aceptadas)
El campo “mandatory” dirá si es obligatorio que tenga valor. De esta manera, si el usuario ingresa datos, se verifican para comprobar su formato aunque el campo no sea obligatorio.

La función podría empezar algo como esto:


/**
*
* Valida campos y los reformatea si procede.
* De momento solo formatea numeros
*/
validaCampos = function( elems_array ){

	var errores = false;

	for( var i in elems_array ){
		var e = elems_array[i]
		var _id = e.id;
		var _valor = jQuery(_id).val().trim();
		var mv = e.minVal != undefined? e.minVal : 0;
		var hayMaxVal = e.maxVal != undefined? true : false;
		var mxv = e.maxVal;

	}//for
}//validador

Dentro del for iremos validando cada una de las entradas del usuario

Una de las opciones que primero vamos a implementar es la de “checkMandatory()” para comprobar que un campo que tiene que tener valor, realmente lo tiene (aunque sea incorrecto…)

Lo que vamos a hacer es definir la función dentro de la función, como una variable


var checkMandatory = function(errorValues){
	//if the value is ErrorValue is considered wrong as well
	var success=true;
	var ev_arr = errorValues != undefined? errorValues : [""];
	if( e.mandatory ){
		//si no hay nada o esta en el array de valores de error.... chasca
		if(_valor.length == 0 || jQuery.inArray(_valor, ev_arr) != -1 ){
			success=false;
			alert( e.msg );
		}
	}
	return success;
};

Le pasaremos un array con valores erróneos. Esto e spara poder decirle: Si tiene un valor que sea uno de estos que te doy, es como si no tuviera nada… Esto es muy útil para los select por ejemplo. Si no nos pasan ningún array, pues le decimos que lo que no nos gusta es la cadena vacía. Entonces, en caso de que el elemento sea obigatorio, miramos que no sea la cadena vacía. Si es así, pues marcamos que no ha habido éxito y alertamos al usuario con el mensaje.

Luego lo que haremos es verificar que el campo tiene un valor aceptado en cuanto a lo que contiene el JSON. Por ejemplo, para tipo “text”, si es obligatorio, nos vale con que tenga algo… pero podríamos haberle definido un minLength, maxLength… etc.

Vamos a meter la verificación del tipo text


switch( e.tipo ){
	case 'text':
		if( !checkMandatory() ) errores=true;

		break;
}

Como se ve, es muy sencillo! solo miramos que tenga contenido o no, según diga el campo “mandatory”. Con esta tónica, también nos vale para los selectores / desplegables. Lo que suelo hacer yo es, cuando creo un select en html es darle a la opción de “–seleccione–” el valor de 0 o de cadena vacía según sea el select… cada uno tiene sus manías. Pues apra eso nos viene bien el array de errores.

Si queremos validar un select, podemos hacer este JSON


{
	id: 		"#selectorDePaises",
	msg: 		"Selecciona un país para el disco",
	tipo: 		"select",
	errorVals 	:['0'],
	mandatory: 	true
}

y la evaluación en el switch podría ser esta


case 'select':
	var ev_arr = e.errorVals != undefined? e.errorVals : ['', '0'];
	if(!checkMandatory(ev_arr)) errores=true;
	break;

Nos aseguramos de que el array tenga un valor, por si acaso… aunque la función checkMandatory() ya lo haga también.

Y así nos podemos eternizar creando tipos de campos…. Por ejemplo, para comprobar que se ha introducido un entero:


{
	id: 		"#capacidad",
	msg: 		"Pon una capacidad",
	tipo: 		"int",
	mandatory: 	true,
	minVal: 	1
}

Aceptamos un minVal para indicar que nos vale un mínimo de valor… podríamos considerar maxVal por ejemplo. Para ver que es positivo, nos vale un minVal de 0 o de 1.


case 'int':
	if( !checkMandatory() ) errores=true;

	if ( isNaN(_valor) ) {
		errores = true;
		alert( e.msg + " - No es un número válido");
	}else{
		//es numero
		_valor = parseInt( _valor );
		if( _valor < mv ){
			errores = true;
			alert( e.msg + " - El valor mínimo es de " + mv);
		}
		if( hayMaxVal && _valor > mxv ){
			errores = true;
			alert( e.msg + " - El valor máximo es de " + mv);
		}

	}
	if(!errores){
		jQuery(_id).val( parseInt(_valor ) );
	}

	break;

En este caso, reasigno el valor al campo a validar. Igual, por paranoia…

Y ya para acabar el ejemplo, podemos meter un tipo “money” para validar que el usuario ha introducido una cantidad que se asemeje a una cantidad de dinero.


case 'money':
	if( !checkMandatory() ) errores=true;

	//reemplazo coma por punto, en españa ponemos coma en lugar de punto
	_valor = _valor.replace(",", ".");
	if( isNaN(_valor) ){
		errores=true;
		alert(e.msg);
	}else{
		//es un numero, le dejo a 'e.decimals' decimales. Por defecto, 2
		var d = e.decimals != undefined? e.decimals : 2;
		_valor = parseFloat(_valor);
		_valor = _valor.toFixed(d);
		if( _valor < mv ){
			alert( e.msg + " - El valor minimo es de " + mv);
			errores = true;
		}
	}
	if(!errores){
		jQuery(_id).val( parseInt(_valor ) );
	}

	break;

Después del switch, vemos si hubo errores. Si los hubo, hacemos lo que haya que hacer (en este caso, resetLoaders() lo que haría es quitar los típicos spinners que dan vueltas mientras hacemos comprobaciones, cargas…) y devolvemos si hubo éxito o no


if(errores) {
	resetLoaders();
	return {success: false };
}

y poco más, luego nos queda ejecutarlo… para ello, definiríamos lo que queremos validar y llamaríamos a la función, por ejemplo:


validaConcierto = function(){

	var elems_array =
		[
			{
				id: 		"#concerts_artist",
				msg: 		"Pon un artista",
				tipo: 		"text",
				mandatory: 	true
			},
			{
				id: 		"#concerts_place",
				msg: 		"Pon un lugar/recinto",
				tipo: 		"text",
				mandatory: 	true
			},
			{
				id: 		"#concerts_capacity",
				msg: 		"Pon una capacidad",
				tipo: 		"int",
				mandatory: 	true,
				minVal: 	1
			},
			{
				id: 		"#concerts_city",
				msg: 		"Pon una ciudad",
				tipo: 		"text",
				mandatory: 	true
			},
			{
				id: 		"#concerts_price",
				msg: 		"Ponle un precio válido. Pon 0 para GRATIS",
				tipo: 		"money",
				mandatory: 	true,
				decimals: 	3,
				minVal: 	0
			},
			{
				id: 		"#concerts_ticket_types",
				msg: 		"Pon el tipo de entradas disponibles",
				tipo: 		"text",
				mandatory: 	true
			},
			{
				id: 		"#concerts_date",
				msg: 		"La fecha no puede estar vacía, utiliza el calendario",
				tipo: 		"text",
				mandatory: 	true
			}
		];

	return validaCampos( elems_array );

}

Qué flecos quedan? Pues validar fechas, por ejemplo y a lo mejor se puede refactorizar el código un poquito para mejorarlo.
El código sería


/**
*
* Valida campos y los reformatea si procede.
* De momento solo formatea numeros
*/
validaCampos = function( elems_array ){

	var errores = false;

	for( var i in elems_array ){
		var e = elems_array[i]
		var _id = e.id;
		var _valor = jQuery(_id).val().trim();
		var mv = e.minVal != undefined? e.minVal : 0;
		var hayMaxVal = e.maxVal != undefined? true : false;
		var mxv = e.maxVal;

		var checkMandatory = function(errorValues){
			//if the value is ErrorValue is considered wrong as well
			var success=true;
			var ev_arr = errorValues != undefined? errorValues : [""];
			if( e.mandatory ){
				//si no hay nada o esta en el array de valores de error.... chasca
				if(_valor.length == 0 || jQuery.inArray(_valor, ev_arr) != -1 ){
					success=false;
					alert( e.msg );
				}
			}
			return success;
		};

		switch( e.tipo ){
			case 'text':
				if( !checkMandatory() ) errores=true;

				break;

			case 'select':
				var ev_arr = e.errorVals != undefined? e.errorVals : [""];
				if(!checkMandatory(ev_arr)) errores=true;

				break;

			case 'int':
				if( !checkMandatory() ) errores=true;

				if ( isNaN(_valor) ) {
					errores = true;
					alert( e.msg + " - No es un número válido");
				}else{
					//es numero
					_valor = parseInt( _valor );
					if( _valor < mv ){
						errores = true;
						alert( e.msg + " - El valor mínimo es de " + mv);
					}
					if( hayMaxVal && _valor > mxv ){
						errores = true;
						alert( e.msg + " - El valor máximo es de " + mv);
					}

				}
				if(!errores){
					jQuery(_id).val( parseInt(_valor ) );
				}

				break;
			case 'money':
				if( !checkMandatory() ) errores=true;

				//reemplazo coma por punto
				_valor = _valor.replace(",", ".");
				if( isNaN(_valor) ){
					errores=true;
					alert(e.msg);
				}else{
					//es un numero, le dejo a 'e.decimals' decimales
					var d = e.decimals != undefined? e.decimals : 2;
					_valor = parseFloat(_valor);
					_valor = _valor.toFixed(d);
					if( _valor < mv ){
						alert( e.msg + " - El valor minimo es de " + mv);
						errores = true;
					}
				}
				if(!errores){
					jQuery(_id).val( parseInt(_valor ) );
				}

				break;
		}
		if(errores) {
			resetLoaders();
			return {success: false };
		}
	}
	return {success: true};
}

Bájatelo de GitHub 🙂