Applications à page unique plus réactives avec AngularJS & Socket.IO Création de la bibliothèque

Ni HTML ni HTTP n'ont été créés pour les applications Web dynamiques. Nous nous basons essentiellement sur des hacks, au-dessus de ceux-ci, pour donner à nos applications une interface utilisateur réactive. AngularJS supprime certaines limitations du langage HTML, ce qui nous permet de créer et de gérer plus facilement le code de l'interface utilisateur. Socket.IO, en revanche, nous aide à envoyer des données du serveur non seulement lorsque le client le demande, mais également lorsque le serveur en a besoin. Dans cet article, je vais vous montrer comment combiner ces deux éléments pour améliorer la réactivité de vos applications à page unique..


introduction

Dans la première partie de ce didacticiel, nous allons créer un service AngularJS réutilisable pour Socket.IO. À cause de ça réutilisable partie, ce sera un peu plus délicat que de simplement utiliser module.service () ou module.factory (). Ces deux fonctions ne sont que du sucre syntaxique en plus des fonctions de bas niveau. module.provider () méthode, que nous utiliserons pour fournir certaines options de configuration. Si vous n'avez jamais utilisé AngularJS auparavant, je vous conseille vivement de suivre au moins le tutoriel officiel et certains des tutoriels ici sur Tuts+.


Préparation: le back-end

Avant de commencer à écrire notre module AngularJS, nous avons besoin d’un back-end simple pour les tests. Si vous connaissez déjà Socket.IO, vous pouvez simplement faire défiler l'écran jusqu'à la fin de cette section, copier le fichier source et passer au point suivant, si ce n'est pas le cas - lisez la suite.

Modules requis

Nous n'aurons besoin que socket.io. Vous pouvez l’installer directement à l’aide de la touche npm commande comme ceci:

npm installer socket.io 

Ou créer un package.json fichier, mettez cette ligne dans le les dépendances section:

"socket.io": "0.9.x" 

Et exécuter le npm installer commander.

Création du serveur Socket.IO

Comme nous n’avons besoin d’aucune infrastructure Web complexe comme Express, nous pouvons créer le serveur à l’aide de Socket.IO:

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

C'est tout ce dont vous avez besoin pour configurer le serveur Socket.IO. Si vous démarrez votre application, vous devriez voir une sortie similaire dans la console:

Et vous devriez pouvoir accéder au socket.io.js fichier dans votre navigateur à http: // localhost: 8080 / socket.io / socket.io.js:

Gestion des connexions

Nous allons gérer toutes les connexions entrantes dans le lien auditeur d'événement du io.sockets objet:

io.sockets.on ('connexion', fonction (socket) ); 

le prise L'attribut transmis au rappel est le client qui s'est connecté et nous pouvons écouter les événements qui s'y trouvent..

Un auditeur basique

Nous allons maintenant ajouter un écouteur d'événements de base dans le rappel ci-dessus. Il enverra les données reçues au client en utilisant le socket.emit () méthode:

 socket.on ('echo', fonction (données) socket.emit ('echo', données);); 

écho est le nom de l'événement personnalisé que nous utiliserons plus tard.

Un auditeur avec remerciement

Nous utiliserons également les remerciements dans notre bibliothèque. Cette fonctionnalité vous permet de passer une fonction en tant que troisième paramètre de socket.emit () méthode. Cette fonction peut être appelée sur le serveur pour renvoyer certaines données au client:

 socket.on ('echo-ack', fonction (données, rappel) rappel (données);); 

Cela vous permet de répondre au client sans qu'il soit obligé d'écouter les événements (ce qui est utile si vous souhaitez simplement demander des données au serveur)..

Notre test est maintenant terminé. Le code devrait ressembler à ceci (c'est le code que vous devriez copier si vous avez omis cette section):

var io = require ('socket.io') (8080); io.sockets.on ('connexion', fonction (socket) socket.on ('echo', fonction (données) socket.emit ('echo', données);); socket.on ('echo-ack ', fonction (données, rappel) rappel (données););); 

Vous devriez maintenant lancer l'application et la laisser en marche avant de continuer avec le reste du tutoriel..


Préparation: le front-end

Nous aurons bien sûr besoin de HTML pour tester notre bibliothèque. Nous devons inclure AngularJS, socket.io.js de notre back-end, notre angular-socket.js bibliothèque et un contrôleur AngularJS de base pour exécuter certains tests. Le contrôleur sera en ligne dans le du document pour simplifier le flux de travail:

           

C'est tout ce dont nous avons besoin pour l'instant, nous reviendrons plus tard sur la balise de script vide car nous n'avons pas encore la bibliothèque..


Création de la bibliothèque AngularJS Socket.IO

Dans cette section, nous allons créer le angular-socket.js bibliothèque. Tout le code doit être inséré dans ce fichier.

Le module

Commençons par créer le module pour notre bibliothèque:

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

Nous n'avons pas de dépendances, donc le tableau dans le deuxième argument de angular.module () est vide, mais ne l'enlevez pas complètement ou vous obtiendrez un $ injecteur: nomod Erreur. Cela se produit parce que la forme à un argument de angular.module () récupère une référence au module déjà existant, au lieu d'en créer un nouveau.

Le fournisseur

Les fournisseurs sont l’un des moyens de créer des services AngularJS. La syntaxe est simple: le premier argument est le nom du service (pas le nom du fournisseur!) Et le second est la fonction constructeur du fournisseur:

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

Options de configuration

Pour rendre la bibliothèque réutilisable, nous devrons autoriser des modifications dans la configuration de Socket.IO. Définissons d’abord deux variables qui contiendront l’URL de la connexion et l’objet de configuration (le code de cette étape va au $ socketProvider () une fonction):

 var ioUrl = "; var ioConfig = ; 

Maintenant, puisque ces variables ne sont pas disponibles en dehors de la $ socketProvider () fonction (ils sont genre de privé), nous devons créer des méthodes (setters) pour les changer. Nous pourrions bien sûr les faire Publique comme ça:

 this.ioUrl = "; this.ioConfig = ; 

Mais:

  1. Nous devrions utiliser Function.bind () plus tard pour accéder au contexte approprié pour ce
  2. Si nous utilisons des setters, nous pouvons valider pour nous assurer que les valeurs appropriées sont définies - nous ne voulons pas mettre faux comme le 'délai de connexion' option

Une liste complète des options pour le client de Socket.IO peut être consultée sur leur wiki GitHub. Nous allons créer un setter pour chacun d'eux et un pour l'URL. Toutes les méthodes se ressemblent, je vais donc expliquer le code de l'une d'elles et mettre le reste ci-dessous.

Définissons la première méthode:

 this.setConnectionUrl = fonction setConnectionUrl (url)  

Il convient de vérifier le type de paramètre passé dans:

 if (typeof url == 'string')  

Si c'est celui auquel nous nous attendions, définissez l'option:

 ioUrl = url; 

Si non, il faut jeter Erreur-type:

  else jeter un nouveau TypeError ('url doit être du type chaîne'); ; 

Pour le reste d'entre eux, nous pouvons créer une fonction d'assistance pour la garder au sec:

 function setOption (nom, valeur, type) if (typeof valeur! = type) renvoie un nouveau TypeError ("'" "+ nom +" "doit être de type" "+ type +" "");  ioConfig [nom] = valeur;  

Il jette juste Erreur-type si le type est incorrect, sinon définit la valeur. Voici le code pour le reste des options:

 this.setResource = fonction setResource (valeur) setOption ('ressource', valeur, 'chaîne'); ; this.setConnectTimeout = fonction setConnectTimeout (valeur) setOption ('délai de connexion', valeur, 'nombre'); ; this.setTryMultipleoutes = fonction setTryMultipleoutes (valeur) setOption ('try multiple transports', valeur, 'boolean'); ; this.setReconnect = fonction setReconnect (valeur) setOption ('reconnect', valeur, 'boolean'); ; this.setReconnectionDelay = fonction setReconnectionDelay (valeur) setOption ('délai de reconnexion', valeur, 'nombre'); ; this.setReconnectionLimit = fonction setReconnectionLimit (valeur) setOption ('limite de reconnexion', valeur, 'nombre'); ; this.setMaxReconnectionAttempts = fonction setMaxReconnectionAttempts (valeur) setOption ('tentatives de reconnexion maximales', valeur, 'nombre'); ; this.setSyncDisconnectOnUnload = fonction setSyncDisconnectOnUnload (valeur) setOption ('sync disconnect on unload', valeur, 'boolean'); ; this.setAutoConnect = fonction setAutoConnect (valeur) setOption ('auto connect', valeur, 'boolean'); ; this.setFlashPolicyPort = fonction setFlashPolicyPort (valeur) setOption ('port de règle flash', valeur, 'nombre'); this.setForceNewConnection = fonction setForceNewConnection (valeur) setOption ('forcer une nouvelle connexion', valeur, 'boolean'); ; 

Vous pouvez le remplacer par un seul setOption () méthode, mais il semble plus facile de taper le nom de l'option en cas de chameau, plutôt que de le transmettre sous forme de chaîne avec des espaces.

La fonction d'usine

Cette fonction créera l'objet de service que nous pourrons utiliser plus tard (par exemple dans les contrôleurs). D'abord, appelons le io () fonction pour se connecter au serveur Socket.IO:

 ceci. $ get = fonction $ socketFactory ($ rootScope) var socket = io (ioUrl, ioConfig); 

Notez que nous assignons la fonction à la $ get propriété de l’objet créé par le fournisseur - c’est important car AngularJS utilise cette propriété pour l’appeler. Nous avons aussi mis $ rootScope comme paramètre. À ce stade, nous pouvons utiliser l'injection de dépendance d'AngularJS pour accéder à d'autres services. Nous allons l'utiliser pour propager les modifications à tous les modèles dans les rappels Socket.IO.

Maintenant, la fonction doit retourner un objet:

 revenir  ; ; 

Nous y mettrons toutes les méthodes pour le service.

le sur() Méthode

Cette méthode attachera un écouteur d'événement à l'objet socket afin que nous puissions utiliser toutes les données envoyées depuis le serveur:

 on: fonction on (événement, rappel)  

Nous allons utiliser Socket.IO socket.on () attacher notre rappel et l'appeler dans AngularJS $ scope. $ apply () méthode. C'est très important, car les modèles ne peuvent être modifiés qu'à l'intérieur:

 socket.on (événement, fonction ()  

Tout d'abord, nous devons copier les arguments dans une variable temporaire pour pouvoir les utiliser plus tard. Les arguments sont bien sûr tout ce que le serveur nous a envoyé:

 var args = arguments; 

Ensuite, nous pouvons appeler notre rappel en utilisant Function.apply () pour lui passer des arguments:

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

Quand priseL'émetteur d'événements appelle la fonction d'écoute qu'il utilise $ rootScope. $ apply () d'appeler le rappel fourni comme deuxième argument du .sur() méthode. De cette façon, vous pouvez écrire vos écouteurs d'événements comme vous le feriez pour n'importe quelle autre application utilisant Socket.IO, mais vous pouvez également modifier les modèles AngularJS..

le de() Méthode

Cette méthode supprimera un ou tous les écouteurs d'événement pour un événement donné. Cela vous aide à éviter les fuites de mémoire et les comportements inattendus. Imaginez que vous utilisez ngRoute et vous attachez quelques auditeurs dans chaque contrôleur. Si l'utilisateur accède à une autre vue, votre contrôleur est détruit, mais l'écouteur d'événements reste attaché. Après quelques navigations et nous aurons une fuite de mémoire.

 off: fonction off (événement, rappel)  

Il suffit de vérifier si le rappeler a été fourni et appel socket.removeListener () ou socket.removeAllListeners ():

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

le émettre() Méthode

C'est la dernière méthode dont nous avons besoin. Comme son nom l'indique, cette méthode enverra des données au serveur:

 emit: fonction emit (événement, données, rappel)  

Socket.IO prenant en charge les accusés de réception, nous vérifierons si le rappeler a été fourni. Si c'était le cas, nous utiliserons le même schéma que dans le sur() méthode pour appeler le rappel à l'intérieur de $ scope. $ apply ():

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

Si il n'y a pas rappeler nous pouvons simplement appeler socket.emit ():

  else socket.emit (événement, données);  

Usage

Pour tester la bibliothèque, nous allons créer un formulaire simple qui enverra des données au serveur et affichera la réponse. Tout le code JavaScript de cette section doit être placé dans la > tag dans le de votre document et tout le HTML va dans sa .

Création du module

Nous devons d'abord créer un module pour notre application:

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

Remarquerez que 'socket.io' dans le tableau, dans le deuxième paramètre, indique à AngularJS que ce module dépend de notre bibliothèque Socket.IO.

La fonction de configuration

Comme nous allons utiliser un fichier HTML statique, nous devons spécifier l’URL de connexion pour Socket.IO. Nous pouvons le faire en utilisant le config () méthode du module:

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

Comme vous pouvez le constater, notre $ socketProvider est automatiquement injecté par AngularJS.

Le controlle

Le contrôleur sera responsable de toute la logique de l'application (l'application est petite, nous n'avons donc besoin que d'une seule):

app.controller ('Ctrl', fonction Ctrl ($ scope, $ socket)  

$ scope est un objet qui contient tous les modèles du contrôleur, c'est la base de la liaison de données bidirectionnelle d'AngularJS. $ socket est notre service Socket.IO.

Tout d'abord, nous allons créer un auditeur pour le 'écho' événement qui sera émis par notre serveur de test:

 $ socket.on ('echo', fonction (données) $ scope.serverResponse = data;); 

Nous allons afficher $ scope.serverResponse plus tard, en HTML, en utilisant les expressions d'AngularJS.

Maintenant, il y aura aussi deux fonctions qui enverront les données - une en utilisant la base émettre() méthode et une en utilisant émettre() avec rappel de confirmation:

 $ scope.emitBasic = function 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 = "; ; ); 

Nous devons les définir comme des méthodes de $ scope afin que nous puissions les appeler du ngClick directive en HTML.

Le HTML

C'est là qu'AngularJS se démarque - nous pouvons utiliser du HTML standard avec des attributs personnalisés pour tout relier..

Commençons par définir le module principal en utilisant un ngApp directif. Placez cet attribut dans le balise de votre document:

 

Cela indique à AngularJS qu’il devrait amorcer votre application à l’aide de la Exemple module.

Après cela, nous pouvons créer un formulaire de base pour envoyer des données au serveur:

 
Réponse du serveur: serverResponse
Réponse du serveur (ACK): serverResponseACK

Nous avons utilisé ici quelques attributs personnalisés et directives AngularJS:

  • ng-contrôleur - lie le contrôleur spécifié à cet élément, vous permettant d'utiliser les valeurs de sa portée
  • ng-model - crée une liaison de données bidirectionnelle entre l'élément et la propriété de portée spécifiée (un modèle), ce qui vous permet d'obtenir des valeurs de cet élément et de les modifier à l'intérieur du contrôleur
  • ng-clic - attache un Cliquez sur écouteur d'événements qui exécute une expression spécifiée (pour en savoir plus sur les expressions AngularJS)

Les doubles accolades sont également des expressions AngularJS, elles seront évaluées (ne vous inquiétez pas, ne pas utiliser JavaScript eval ()) et leur valeur y sera insérée.

Si vous avez tout fait correctement, vous devriez pouvoir envoyer des données au serveur en cliquant sur les boutons et voir la réponse dans le champ approprié.

Mots clés.


En résumé

Dans cette première partie du didacticiel, nous avons créé la bibliothèque Socket.IO pour AngularJS, qui nous permettra de tirer parti de WebSockets dans nos applications à page unique. Dans la deuxième partie, je vais vous montrer comment améliorer la réactivité de vos applications en utilisant cette combinaison..