Comunidad de diseño web y desarrollo en internet online

MVC en Javascript, con AJAX y jQuery, Parte 2: Controladores

En el tutorial anterior aprendimos a crear una interfaz base que usa las pestañas de Jquery UI como navegación y el plugin Jquery Address que permite utilizar los botones del navegador atrás y adelante, compartir la URL, guardarla en favoritos, etc. como hacemos cuando navegamos entre páginas normalmente pero en este caso para la navegación AJAX.

En HTML5 ya hay nuevas tecnologías para hacer el cambio de URL sin recargar, pero mientras tanto usamos Jquery address y similares

En este nuevo tutorial usaremos objetos en javascript para ordenar el código que se ejecutará entre las acciones de una pestaña u otra.

Asumiremos que cada pestaña es un "Controlador" y dentro de ellas ejecutaremos "Acciones" tal como lo hacemos en Frameworks como Symfony, Codeigniter, etc. Por ejemplo cuando accedemos a noticias/lista, internamente estamos usando el controlador Noticias y la acción "lista". En este caso haremos algo similar pero en javascript.

Modelo Vista Controlador en Javascript

Para empezar podemos descargar el código. Las bibliotecas que usaremos en esta ocasión serán las mismas, sólo que agregué main.js.

El archivo main.js contiene algunas funciones / clases en javascript que actuan como un sencillo framework de rutas para permitir el llamado a nuestros controladores / acciones cada vez que cambie el hash URL.

Para ello en nuestro código anterior cambiaremos el contenido de la funcion / callback $.address.change a esto:

Código :

$.address.change(function(event)
{  
   App.handleRequest(event, 'ruylopez');
});
Como se imaginarán App.handleRequest es el método encargado de manejar la petición o evento (event), el segundo parámetro (en este caso: 'ruylopez') es el controlador que será invocado por defecto.

Ahora cada vez que se cambie la URL el callback $.address.change llamará a App.handleRequest() que se encargará de manejar la petición por nosotros, llamando a los controladores y acciones adecuados; ahora bien, todo lo que resta hacer es... exacto! Escribir los controladores.


Escribiremos un controlador sencillo

Código :

function RuylopezController ()
{
   var Ctr = this;
   this.name = "ruylopez";

   this.init = function ()
   {
      dummy_log ('RuylopezController.init()');
   }

   this.indexAction = function (Request)
   {
      // Seleccionamos la pestaña "ruylopez"
      $("#tabs").tabs("select", Ctr.name);
      dummy_log ('RuylopezController.indexAction()');
   }
}

Puntos a destacar:

  • Se utiliza una pequeña convención para el nombre del controlador: Primera letra de la URL en mayúscula seguida por la palabra "Controller"
  • Dado que en Javascript no tenemos "constructores" como tal, decidí "englobar" el código que debe ejecutarse al cargar el controlador por primera vez en la función "init", escribir esta función es opcional y su código se ejecutará sólo una vez como ya mencioné.
  • También podemos usar la función "preExecute" como callback, se ejecutará antes de llamar a una acción, la diferencia con "init" es que "preExecute" si se ejecutará varias veces, una vez por cada llamado a una acción de dicho controlador.
  • Las acciones son en minúscula y van seguidas por el sufijo "Action"
  • Como se puede ver la opción por defecto es "indexAction" pero se pueden usar otras, como veremos más adelante en este tutorial.
  • En Javascript "this" se refiere al objeto que invocó a una función y no necesariamente al objeto o controlador como tal, como sucede en PHP, por esto usamos "var Ctr = this;" cuando querramos referirnos a una propiedad o método del controlador dentro de éste usaremos "Ctr" y no this para evitar errores.
  • Podemos declarar las variables y métodos que queramos dentro del controlador.
En el código fuente de ejemplo.html están los otros controladores que son muy similares al que copié acá. Pueden ver en el primer ejemplo funcionando.

Manejando URLs más complejas

Ahora, los ejemplos que hemos visto hasta ahora son muy sencillos, supongamos que queremos llamar a una acción más compleja, por ejemplo, dentro de la pestaña "siciliana" queremos analizar la variante del dragon y la variante najdorf, haríamos esto en el HTML:

Código :

<h3>Variantes</h3>
               
<ul>
   <li><a href="#siciliana/dragon">Drag&oacute;n</a></li>
   <li><a href="#siciliana/najdorf">Najdorf</a></li>
</ul>
Ahora cuando alguien haga clic en Najdorf automáticamente se llamaría a SicilianaController.najdorfAction() vamos a agregarlo en el código del ejemplo:

Código :

this.najdorfAction = function (Request)
{
   dummy_log ('SicilianaController.najdorfAction()');
}
Y por supuesto haríamos otra acción "dragonAction" para el link a la variante dragón.

Nota :

Noten que hasta ahora el contenido de las acciones es realmente muy sencillo, salvo cambiar la pestaña seleccionada con la acción preExecute, no hacemos otra cosa, entonces ¿Cual es la utilidad de estos ejemplos? Fácil: ordenar nuestro código dentro de las acciones podemos hacer todo lo que siempre hacemos: Ocultar y mostrar información, hacer animaciones con Jquery, llamadas a AJAX, etc.
Como ya hemos visto las URLs tienen el siguiente patrón: controlador/accion.

Pero, ¿Qué pasa si necesitamos parámetros?. Sencillo: supongamos que queremos mostrar links a los ejemplos de la variante najdorf, hacemos esto en el HTML:

Código :

<div id="najdorf">
<h3>Ejemplos variante najdorf: </h3>
<ul>
   <li><a href="#siciliana/ejemplos/variante/najdorf/numero/1">Ejemplo 1</a></li>
   <li><a href="#siciliana/ejemplos/variante/najdorf/numero/2">Ejemplo 2</a></li>
</ul>
</div>
Noten que estas nuevas URLs siguen el siguiente patrón:

controlador/accion/nombre_parametro_1/valor_parametro_1/.../nombre_parametro_N/valor_parametro_N

Ahora creamos la acción para dichas URLs:

Código :

this.ejemplosAction = function (Request)
{
   var variante = Request.Url.getParam('variante');
   var num = Request.Url.getParam('numero');
   dummy_log('Ejemplo variante ' + variante + ' numero: ' + num);
}
Vemos que la función Request.Url.getParam() nos permite obtener el valor del parámetro o parámetros, en este caso "variante" y "numero" y podríamos utilizarlos para las peticiones $.ajax, etc.

Ahora, para mejorar nuestro ejemplo, volveremos atrás y reemplazaremos estas funciones de sicilianaController:

Código :

this.preExecute = function ()
{
   $("#tabs").tabs("select", Ctr.name);
   $('#najdorf').hide();
   dummy_log ('SicilianaController.preExecute()');
}

this.najdorfAction = function (Request)
{
   dummy_log ('SicilianaController.najdorfAction()');
   $('#najdorf').fadeIn();
}
Como podemos ver ahora la capa con los ejemplos de la najdorf sólo se mostrará si hacemos click en "Najdorf" mejorando así nuestra interfaz.

Puedes ver el ejemplo funcionando aquí

Otras funciones

Nuestros métodos preExecute y *Action recibirán siempre como parámetro el objeto Request este objeto tiene varios métodos y propiedades útiles:

Código :

Request.Url.getUrl() //Obtiene la URL completa
Request.Url.getUrlForPag() //Obtiene la URL removiendo la Regex /page/[numero] al final, por ej: noticias/lista/page/5 devolvería: noticias/lista.
Request.Url.getModule() //Obtiene el nombre del módulo, por ej "noticias"
Request.Url.getAction() //Obtiene el nombre de la acción, por ej "lista"
Request.Url.getParams() //Obtiene todos los parametros en un array
Request.Url.getParam("param") //Obtiene el valor de un parametro
Request.Url.hasParam() //Revisa si un parametro existe

App.getCurrentController() //Obtiene el controlador que se esta ejecutando actualmente, o el ultimo en ejecutarse
App.getCurrentModule() //Obtiene el nombre del modulo actual
App.refresh() //Vuelve a ejecutar la ultima petición invocada
App.getController(nombre) //Obtiene un controlador manualmente, util porque esto trabaja como singleton y permite usar "init" adecuadamente
También se pueden agregar nuevos métodos o modificar el código a gusto.

Un último detalle ¿Qué pasa con los eventos click, change, etc?

Como estos eventos por lo general no tienen y de hecho no deben tener "hash": no vamos a cambiar la URL por cada acción que haga el usuario, por ejemplo si le da click en "guardar" NO debemos cambiar la URL sólo guardar los datos y ya (si lo estamos haciendo con ajax, por supuesto). Entonces estos eventos los seguiremos haciendo normalmente, por lo general yo lo coloco en la función init() del controlador correspondiente.

Código :

this.init = function ()
{
   dummy_log ('SicilianaController.init()');   
   $('#btn_enviar_comentarios').click (function () {
      Ctr.enviarComentarios(); // << se llama al metodo con el codigo
      return false;
   })
}

this.enviarComentarios = function ()
{
   alert ('abrir modal con form de comentarios');
}

Por lo general, como en el ejemplo, creo un método en el mismo controlador, para mantener la lógica y el orden, pero ya queda de parte de cada quien cómo manejar esta parte.

Ver el ejemplo (noten el link al final)

Conclusión

Espero más que darles un código ready-to-use darles una idea de cómo podemos hacer que javascript apeste menos y sea más funcional y reusable y espero que les haya gustado el tutorial.

El código sólo lo he probado en Firefox y Chrome hasta ahora por lo que quizás haya que modificar algo para IE6 IE7 o IE8.

Puedes descargar el código completo aquí o verlo funcionando

¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.

Publica tu comentario

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate