Aplicaciones más sensibles de una sola página con AngularJS y Socket.IO creando la biblioteca

Ni HTML ni HTTP fueron creados para aplicaciones web dinámicas. Básicamente, confiamos en los hacks, además de los hacks para dar a nuestras aplicaciones una interfaz de usuario receptiva. AngularJS elimina algunas limitaciones del HTML, lo que nos permite crear y administrar el código de la interfaz de usuario con mayor facilidad. Socket.IO, por otro lado, nos ayuda a enviar datos desde el servidor no solo cuando el cliente lo solicita, sino también cuando el servidor lo necesita. En este artículo, le mostraré cómo combinar estos dos, para mejorar la capacidad de respuesta de sus aplicaciones de una sola página..


Introducción

En la primera parte de este tutorial crearemos un servicio AngularJS reutilizable para Socket.IO. Por eso reutilizable parte, esto será un poco más complicado que solo usar módulo.servicio () o módulo.factory (). Estas dos funciones son solo azúcar sintáctica sobre el nivel más bajo módulo.proveedor () Método, que utilizaremos para proporcionar algunas opciones de configuración. Si nunca ha usado AngularJS antes, le recomiendo que siga al menos el tutorial oficial y algunos de los tutoriales aquí en Tuts+.


Preparación: El Back-End

Antes de comenzar a escribir nuestro módulo AngularJS, necesitamos un back-end simple para las pruebas. Si ya está familiarizado con Socket.IO, puede desplazarse hasta el final de esta sección, copiar la fuente de back-end y pasar a la siguiente, si no, siga leyendo..

Módulos requeridos

Solo necesitaremos socket.io. Puede instalarlo directamente usando el npm comando así:

npm instalar socket.io 

O crear un paquete.json archivo, ponga esta línea en el dependencias sección:

"socket.io": "0.9.x" 

Y ejecutar el npm instalar mando.

Creando el Servidor Socket.IO

Como no necesitamos ningún marco web complicado como Express, podemos crear el servidor utilizando Socket.IO:

var io = require ('socket.io') (8080); 

Eso es todo lo que necesita para configurar el servidor Socket.IO. Si inicia su aplicación, debería ver un resultado similar en la consola:

Y deberías poder acceder a la socket.io.js archivo en su navegador en http: // localhost: 8080 / socket.io / socket.io.js:

Manejo de conexiones

Manejaremos todas las conexiones entrantes en el conexión oyente de eventos del io.sockets objeto:

io.sockets.on ('conexión', función (socket) ); 

los enchufe atributo pasado a la devolución de llamada es el cliente que se conectó y podemos escuchar los eventos en él.

Un oyente básico

Ahora agregaremos un detector de eventos básico en la devolución de llamada anterior. Enviará los datos recibidos, de vuelta al cliente utilizando el socket.emit () método:

 socket.on ('echo', función (datos) socket.emit ('echo', datos);); 

eco es el nombre del evento personalizado que usaremos más adelante.

Un oyente con reconocimiento

También utilizaremos agradecimientos en nuestra biblioteca. Esta característica le permite pasar una función como el tercer parámetro de la socket.emit () método. Se puede llamar a esta función en el servidor para enviar algunos datos al cliente:

 socket.on ('echo-ack', función (datos, devolución de llamada) devolución de llamada (datos);); 

Esto le permite responder al cliente sin requerir que escuche ningún evento (lo cual es útil si solo desea solicitar algunos datos del servidor).

Ahora nuestro back-end de prueba está completo. El código debería tener este aspecto (Este es el código que debe copiar si omite esta sección.):

var io = require ('socket.io') (8080); io.sockets.on ('connection', function (socket) socket.on ('echo', function (data) socket.emit ('echo', data);); socket.on ('echo-ack ', función (datos, devolución de llamada) devolución de llamada (datos););); 

Ahora debe ejecutar la aplicación y dejarla en ejecución antes de continuar con el resto del tutorial..


Preparación: El Front-End

Por supuesto necesitaremos algo de HTML para probar nuestra biblioteca. Tenemos que incluir AngularJS, socket.io.js de nuestro back-end, nuestro angular-socket.js Biblioteca y un controlador AngularJS básico para ejecutar algunas pruebas. El controlador estará en línea en el del documento para simplificar el flujo de trabajo:

           

Esto es todo lo que necesitamos por ahora, volveremos a la etiqueta de secuencia de comandos vacía más adelante ya que aún no tenemos la biblioteca.


Creando la biblioteca AngularJS Socket.IO

En esta sección crearemos el angular-socket.js biblioteca. Todo el código debe ser insertado en este archivo.

El módulo

Vamos a empezar con la creación del módulo para nuestra lib:

var module = angular.module ('socket.io', []); 

No tenemos ninguna dependencia, por lo que la matriz en el segundo argumento de angular.module () está vacío, pero no lo quite completamente o obtendrá un $ inyector: nomod error. Esto sucede porque la forma de un argumento de angular.module () recupera una referencia al módulo ya existente, en lugar de crear uno nuevo.

El proveedor

Los proveedores son una de las formas de crear servicios de AngularJS. La sintaxis es simple: el primer argumento es el nombre del servicio (¡no el nombre del proveedor!) Y el segundo es la función constructora para el proveedor:

module.provider ('$ socket', $ socketProvider () ); 

Opciones de configuración

Para hacer que la biblioteca sea reutilizable, tendremos que permitir cambios en la configuración de Socket.IO. Primero definamos dos variables que contendrán la URL para la conexión y el objeto de configuración (el código en este paso va a la $ socketProvider () función):

 var ioUrl = "; var ioConfig = ; 

Ahora ya que estas variables no están disponibles fuera de la $ socketProvider () función (son una especie de privado), tenemos que crear métodos (setters) para cambiarlos. Podríamos, por supuesto, simplemente hacerlos público Me gusta esto:

 this.ioUrl = "; this.ioConfig = ; 

Pero:

  1. Tendríamos que usar Function.bind () Más tarde para acceder al contexto apropiado para esta
  2. Si usamos configuradores, podemos validar para asegurarnos de que se configuren los valores correctos, no queremos poner falso como el 'tiempo de espera de conexión' opción

Una lista completa de opciones para el Cliente de Socket.IO se puede ver en su wiki de GitHub. Crearemos un setter para cada uno de ellos más uno para la URL. Todos los métodos son similares, por lo que explicaré el código de uno de ellos y colocaré el resto a continuación..

Vamos a definir el primer método:

 this.setConnectionUrl = función setConnectionUrl (url)  

Debe comprobar el tipo de parámetro pasado en:

 if (typeof url == 'string')  

Si es el que esperábamos, configura la opción:

 ioUrl = url; 

Si no, debe tirar. Error de tecleado:

  else throw new TypeError ('url debe ser de tipo string'); ; 

Para el resto de ellos, podemos crear una función de ayuda para mantenerlo SECO:

 función setOption (nombre, valor, tipo) si (tipo de valor! = tipo) lanzar un nuevo tipo de error de tipo ("'" + nombre + "' debe ser de tipo '" + tipo + "'");  ioConfig [nombre] = valor;  

Solo tira Error de tecleado si el tipo es incorrecto, de lo contrario establece el valor. Aquí está el código para el resto de las opciones:

 this.setResource = función setResource (valor) setOption ('recurso', valor, 'cadena'); ; this.setConnectTimeout = función setConnectTimeout (valor) setOption ('tiempo de espera de conexión', valor, 'número'); ; this.setTryMultipleTransports = función setTryMultipleTransports (value) setOption ('try multiple transports', value, 'boolean'); ; this.setReconnect = función setReconnect (value) setOption ('reconnect', value, 'boolean'); ; this.setReconnectionDelay = function setReconnectionDelay (value) setOption ('retardo de reconexión', valor, 'número'); ; this.setReconnectionLimit = función setReconnectionLimit (valor) setOption ('límite de reconexión', valor, 'número'); ; this.setMaxReconnectionAttempts = función setMaxReconnectionAttempts (valor) setOption ('intentos máximos de reconexión', valor, 'número'); ; this.setSyncDisconnectOnUnload = función setSyncDisconnectOnUnload (value) setOption ('sync disconnect on unload', value, 'boolean'); ; this.setAutoConnect = función setAutoConnect (value) setOption ('auto connect', value, 'boolean'); ; this.setFlashPolicyPort = función setFlashPolicyPort (valor) setOption ('puerto de política de flash', valor, 'número'); this.setForceNewConnection = función setForceNewConnection (value) setOption ('force new connection', value, 'boolean'); ; 

Podrías reemplazarlo con una sola. setOption () Método, pero parece más fácil escribir el nombre de la opción en el caso del camello, en lugar de pasarlo como una cadena con espacios.

La función de fábrica

Esta función creará el objeto de servicio que podemos usar más adelante (por ejemplo, en los controladores). Primero, llamemos al io () Función para conectarse al servidor Socket.IO:

 este. $ get = function $ socketFactory ($ rootScope) var socket = io (ioUrl, ioConfig); 

Tenga en cuenta que estamos asignando la función a la $ obtener propiedad del objeto creado por el proveedor - esto es importante ya que AngularJS usa esa propiedad para llamarlo. Tambien ponemos $ rootScope como su parametro. En este punto, podemos utilizar la inyección de dependencia de AngularJS para acceder a otros servicios. Lo utilizaremos para propagar cambios a cualquier modelo en las devoluciones de llamada de Socket.IO.

Ahora la función necesita devolver un objeto:

 regreso  ; ; 

Pondremos todos los métodos para el servicio en ella..

los en() Método

Este método adjuntará un detector de eventos al objeto socket, por lo que podemos utilizar cualquier información enviada desde el servidor:

 on: function on (event, callback)  

Utilizaremos Socket.IO's. socket.on () para adjuntar nuestra devolución de llamada y llamarlo en AngularJS's $ scope. $ apply () método. Esto es muy importante, porque los modelos solo pueden modificarse dentro de él:

 socket.on (evento, función ()  

Primero, tenemos que copiar los argumentos a una variable temporal para poder usarlos más adelante. Los argumentos son, por supuesto, todo lo que el servidor nos envió:

 var args = argumentos; 

A continuación, podemos llamar a nuestro callback usando Function.apply () para pasarle argumentos:

 $ rootScope. $ apply (function () callback.apply (socket, args);); ); , 

Cuando enchufeEl emisor de eventos llama a la función de escucha que utiliza. $ rootScope. $ apply () para llamar a la devolución de llamada proporcionada como el segundo argumento a la .en() método. De esta manera, puede escribir sus detectores de eventos como lo haría para cualquier otra aplicación que use Socket.IO, pero puede modificar los modelos de AngularJS en ellos..

los apagado() Método

Este método eliminará uno o todos los escuchas de eventos para un evento determinado. Esto le ayuda a evitar pérdidas de memoria y comportamientos inesperados. Imagina que estás usando ngRuta y se adjuntan pocos oyentes en cada controlador. Si el usuario navega a otra vista, su controlador se destruye, pero el detector de eventos permanece conectado. Después de algunas navegaciones y tendremos una pérdida de memoria..

 off: función off (evento, devolución de llamada)  

Solo tenemos que comprobar si el llamar de vuelta fue proporcionado y llamar socket.removeListener () o socket.removeAllListeners ():

 if (typeof callback == 'function') socket.removeListener (event, callback);  else socket.removeAllListeners (event); , 

los emitir() Método

Este es el último método que necesitamos. Como su nombre lo sugiere, este método enviará datos al servidor:

 emitir: función emit (evento, datos, devolución de llamada)  

Dado que Socket.IO admite reconocimientos, verificaremos si el llamar de vuelta fue dado. Si lo fuera, usaremos el mismo patrón que en el en() método para llamar a la devolución de llamada dentro de $ scope. $ apply ():

 if (typeof callback == 'function') socket.emit (event, data, function () var args = argumentos; $ rootScope. $ apply (function () callback.apply (socket, args);); ); 

Si no hay llamar de vuelta solo podemos llamar socket.emit ():

  else socket.emit (evento, datos);  

Uso

Para probar la biblioteca, crearemos un formulario simple que enviará algunos datos al servidor y mostrará la respuesta. Todo el código JavaScript en esta sección debe ir en el > etiqueta en el de su documento y todo HTML va en su .

Creando el Módulo

Primero tenemos que crear un módulo para nuestra aplicación:

var app = angular.module ('example', ['socket.io']); 

Darse cuenta de 'socket.io' en la matriz, en el segundo parámetro, le dice a AngularJS que este módulo depende de nuestra biblioteca Socket.IO.

La función de configuración

Dado que ejecutaremos desde un archivo HTML estático, debemos especificar la URL de conexión para Socket.IO. Podemos hacer esto usando el config () Método del módulo:

app.config (function ($ socketProvider) $ socketProvider.setConnectionUrl ('http: // localhost: 8080');); 

Como puedes ver, nuestra $ socketProvider es inyectado automáticamente por AngularJS.

El controlador

El controlador será responsable de toda la lógica de la aplicación (la aplicación es pequeña, por lo que solo necesitamos una):

app.controller ('Ctrl', función Ctrl ($ scope, $ socket)  

$ alcance es un objeto que contiene todos los modelos del controlador, es la base del enlace de datos bidireccional de AngularJS. $ socket es nuestro servicio Socket.IO.

Primero, crearemos un oyente para el 'eco' evento que será emitido por nuestro servidor de prueba:

 $ socket.on ('echo', function (data) $ scope.serverResponse = data;); 

Vamos a mostrar $ scope.serverResponse Más tarde, en HTML, usando las expresiones de AngularJS..

Ahora también habrá dos funciones que enviarán los datos, una utilizando la función básica. emitir() método y uno usando emitir() con confirmación de devolución de llamada:

 $ scope.emitBasic = función emitBasic () $ socket.emit ('echo', $ scope.dataToSend); $ scope.dataToSend = ";; $ scope.emitACK = function emitACK () $ socket.emit ('echo-ack', $ scope.dataToSend, function (data) $ scope.serverResponseACK = data;); $ scope.dataToSend = "; ; ); 

Tenemos que definirlos como métodos de $ alcance para que podamos llamarlos desde el Haga clic en directiva en HTML.

El HTML

Aquí es donde brilla AngularJS: podemos usar HTML estándar con algunos atributos personalizados para unir todo.

Empecemos por definir el módulo principal usando un ngApp directiva. Coloque este atributo en el etiqueta de su documento:

 

Esto le dice a AngularJS que debe arrancar su aplicación usando el ejemplo módulo.

Después de eso, podemos crear un formulario básico para enviar datos al servidor:

 
Respuesta del servidor: serverResponse
Respuesta del servidor (ACK): serverResponseACK

Usamos algunos atributos personalizados y directivas AngularJS allí:

  • controlador ng - une el controlador especificado a este elemento, permitiéndole usar valores de su alcance
  • modelo ng - crea un enlace de datos bidireccional entre el elemento y la propiedad de alcance especificada (un modelo), que le permite obtener valores de este elemento, así como modificarlo dentro del controlador
  • ng-click - adjunta un hacer clic detector de eventos que ejecuta una expresión específica (lea más sobre las expresiones AngularJS)

Las llaves dobles son también expresiones AngularJS, serán evaluadas (no se preocupe, no use JavaScript). eval ()) y su valor será insertado allí.

Si ha hecho todo correctamente, debería poder enviar datos al servidor haciendo clic en los botones y ver la respuesta en la dirección correspondiente.

etiquetas.


En resumen

En esta primera parte del tutorial, hemos creado la biblioteca Socket.IO para AngularJS que nos permitirá aprovechar WebSockets en nuestras aplicaciones de una sola página. En la segunda parte, le mostraré cómo puede mejorar la capacidad de respuesta de sus aplicaciones con esta combinación.