Altre applicazioni a singola pagina reattive con AngularJS & Socket.IO creazione della libreria

Né HTML né HTTP sono stati creati per applicazioni web dinamiche. Fondamentalmente facciamo affidamento sugli hack, oltre agli hack per fornire alle nostre app un'interfaccia utente reattiva. AngularJS rimuove alcune limitazioni dall'HTML, permettendoci di creare e gestire più facilmente il codice dell'interfaccia utente. Socket.IO, d'altra parte ci aiuta a inviare dati dal server non solo quando il client lo richiede, ma anche quando il server ha bisogno di farlo. In questo articolo ti mostrerò come combinare questi due elementi per migliorare la reattività delle tue app a pagina singola.


introduzione

Nella prima parte di questo tutorial creeremo un servizio AngularJS riutilizzabile per Socket.IO. Per questo motivo riutilizzabile parte, questo sarà un po 'più complicato del semplice utilizzo module.service () o module.factory (). Queste due funzioni sono solo zucchero sintattico in cima al livello più basso module.provider () metodo, che useremo per fornire alcune opzioni di configurazione. Se non hai mai usato AngularJS prima, ti consiglio vivamente di seguire almeno il tutorial ufficiale e alcuni tutorial qui su Tuts+.


Preparazione: il back-end

Prima di iniziare a scrivere il nostro modulo AngularJS, abbiamo bisogno di un semplice back-end per i test. Se hai già familiarità con Socket.IO puoi semplicemente scorrere verso il basso fino alla fine di questa sezione, copiare la sorgente di back-end e procedere alla successiva, in caso contrario - continua a leggere.

Moduli richiesti

Avremo solo bisogno socket.io. Puoi installarlo direttamente usando il npm comando come questo:

npm install socket.io 

O creare un package.json file, inserisci questa riga nel file dipendenze sezione:

"socket.io": "0.9.x" 

Ed esegui il installazione di npm comando.

Creazione del server Socket.IO

Dal momento che non abbiamo bisogno di nessun complicato framework web come Express, possiamo creare il server usando Socket.IO:

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

Questo è tutto ciò che serve per configurare il server Socket.IO. Se avvii la tua app, dovresti vedere un output simile nella console:

E dovresti essere in grado di accedere a socket.io.js file nel browser su http: // localhost: 8080 / socket.io / socket.io.js:

Gestione dei collegamenti

Gestiremo tutte le connessioni in entrata nel connessione ascoltatore di eventi del io.sockets oggetto:

io.sockets.on ('connection', function (socket) ); 

Il presa di corrente l'attributo passato al callback è il client che si è connesso e possiamo ascoltare gli eventi su di esso.

Un ascoltatore di base

Ora aggiungeremo un listener di eventi di base nella richiamata sopra. Invierà i dati ricevuti, di nuovo al client usando il socket.emit () metodo:

 socket.on ('echo', function (data) socket.emit ('echo', data);); 

eco è il nome dell'evento personalizzato che useremo in seguito.

Un ascoltatore con riconoscimento

Useremo anche i riconoscimenti nella nostra biblioteca. Questa funzione consente di passare una funzione come terzo parametro di socket.emit () metodo. Questa funzione può essere richiamata sul server per inviare alcuni dati al client:

 socket.on ('echo-ack', funzione (dati, callback) callback (dati);); 

Ciò consente di rispondere al client senza che sia necessario ascoltare gli eventi (utile se si desidera solo richiedere alcuni dati dal server).

Ora il nostro back-end di test è completo. Il codice dovrebbe apparire come questo (questo è il codice che dovresti copiare se hai omesso questa sezione):

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 ', funzione (dati, callback) callback (dati););); 

Ora dovresti eseguire l'app e lasciarla in esecuzione prima di procedere con il resto del tutorial.


Preparazione: il front-end

Ovviamente avremo bisogno di un codice HTML per testare la nostra libreria. Dobbiamo includere AngularJS, socket.io.js dal nostro back-end, il nostro angolari-socket.js libreria e un controller AngularJS di base per eseguire alcuni test. Il controller sarà inline nel del documento per semplificare il flusso di lavoro:

           

Questo è tutto ciò di cui abbiamo bisogno per ora, torneremo al tag script vuoto più tardi poiché non abbiamo ancora la libreria.


Creazione della libreria Socket.IO AngularJS

In questa sezione creeremo il angolari-socket.js biblioteca. Tutto il codice deve essere inserito in questo file.

Il modulo

Iniziamo con la creazione del modulo per la nostra libreria:

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

Non abbiamo alcuna dipendenza, quindi la matrice nel secondo argomento di angular.module () è vuoto, ma non rimuoverlo completamente o si otterrà un $ Iniettore: nomod errore. Questo accade perché la forma a un argomento di angular.module () recupera un riferimento al modulo già esistente, invece di crearne uno nuovo.

Il fornitore

I provider sono uno dei modi per creare servizi AngularJS. La sintassi è semplice: il primo argomento è il nome del servizio (non il nome del provider!) E il secondo è la funzione di costruzione per il provider:

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

Opzioni di configurazione

Per rendere riutilizzabile la libreria, dovremo consentire le modifiche nella configurazione di Socket.IO. Per prima cosa definiamo due variabili che terranno l'URL per la connessione e l'oggetto di configurazione (il codice in questo passo va al $ SocketProvider () funzione):

 var ioUrl = "; var ioConfig = ; 

Ora dal momento che queste variabili non sono disponibili al di fuori del $ SocketProvider () funzione (sono una specie di privato), dobbiamo creare metodi (setter) per cambiarli. Potremmo, naturalmente, solo farli pubblico come questo:

 this.ioUrl = "; this.ioConfig = ; 

Ma:

  1. Dovremmo usare Function.bind () successivamente per accedere al contesto appropriato per Questo
  2. Se usiamo setter, possiamo validare per assicurarci che i valori corretti siano impostati - non vogliamo metterli falso come il 'collega il timeout' opzione

Un elenco completo delle opzioni per il client Socket.IO può essere visto sul proprio wiki GitHub. Creeremo un setter per ciascuno di essi più uno per l'URL. Tutti i metodi sono simili, quindi spiegherò il codice per uno di essi e riporterà il resto in basso.

Definiamo il primo metodo:

 this.setConnectionUrl = function setConnectionUrl (url)  

Dovrebbe controllare il tipo di parametro passato in:

 if (typeof url == 'string')  

Se è quello che ci aspettavamo, imposta l'opzione:

 ioUrl = url; 

In caso contrario, dovrebbe gettare TypeError:

  else lanciare un nuovo TypeError ('url deve essere di tipo stringa'); ; 

Per il resto, possiamo creare una funzione di supporto per mantenerla ASCIUTTA:

 function setOption (name, value, type) if (typeof value! = type) throw new TypeError ("'" + name + "' deve essere di tipo '" + type + "'");  ioConfig [nome] = valore;  

Semplicemente getta TypeError se il tipo è sbagliato, altrimenti imposta il valore. Ecco il codice per il resto delle opzioni:

 this.setResource = function setResource (value) setOption ('resource', value, 'string'); ; this.setConnectTimeout = function setConnectTimeout (value) setOption ('connect timeout', valore, 'numero'); ; this.setTryMultipleTransports = function setTryMultipleTransports (value) setOption ('prova più trasporti', valore, 'boolean'); ; this.setReconnect = function setReconnect (value) setOption ('riconnetti', valore, 'booleano'); ; this.setReconnectionDelay = function setReconnectionDelay (valore) setOption ('ritardo di riconnessione', valore, 'numero'); ; this.setReconnectionLimit = function setReconnectionLimit (valore) setOption ('limite di riconnessione', valore, 'numero'); ; this.setMaxReconnectionAttempts = function setMaxReconnectionAttempts (value) setOption ('max riconnessione tentativi', valore, 'numero'); ; this.setSyncDisconnectOnUnload = function setSyncDisconnectOnUnload (value) setOption ('sync disconnect on unload', valore, 'boolean'); ; this.setAutoConnect = function setAutoConnect (value) setOption ('auto connect', valore, 'boolean'); ; this.setFlashPolicyPort = function setFlashPolicyPort (value) setOption ('flash policy port', valore, 'numero'); this.setForceNewConnection = function setForceNewConnection (value) setOption ('forza nuova connessione', valore, 'boolean'); ; 

Potresti sostituirlo con un singolo setOption () metodo, ma sembra più facile digitare il nome dell'opzione in caso cammello, piuttosto che passarlo come una stringa con spazi.

La funzione di fabbrica

Questa funzione creerà l'oggetto servizio che potremo utilizzare in seguito (ad esempio nei controller). Per prima cosa, chiamiamo il io () funzione per connettersi al server Socket.IO:

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

Si noti che stiamo assegnando la funzione a $ get proprietà dell'oggetto creato dal provider - questo è importante dal momento che AngularJS usa quella proprietà per chiamarla. Abbiamo anche messo $ rootScope come il suo parametro. A questo punto, possiamo utilizzare l'iniezione delle dipendenze di AngularJS per accedere ad altri servizi. Lo useremo per propagare le modifiche a qualsiasi modello nei callback Socket.IO.

Ora la funzione deve restituire un oggetto:

 ritorno  ; ; 

Metteremo tutti i metodi per il servizio in esso.

Il sopra() Metodo

Questo metodo collegherà un listener di eventi all'oggetto socket, in modo che possiamo utilizzare qualsiasi dato inviato dal server:

 on: function on (event, callback)  

Useremo Socket.IO's socket.on () per allegare la nostra richiamata e chiamarla in AngularJS $ Portata. Applicare $ () metodo. Questo è molto importante, perché i modelli possono essere modificati solo all'interno di esso:

 socket.on (event, function ()  

Per prima cosa, dobbiamo copiare gli argomenti in una variabile temporanea in modo che possiamo usarli in seguito. Gli argomenti sono ovviamente tutto ciò che il server ci ha inviato:

 var args = argomenti; 

Successivamente, possiamo chiamare la nostra callback usando Function.apply () per passare argomenti ad esso:

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

quando presa di correnteL'evento emettitore chiama la funzione listener che usa $ RootScope. Applicare $ () chiamare il callback fornito come secondo argomento per il .sopra() metodo. In questo modo puoi scrivere i tuoi ascoltatori di eventi come faresti per qualsiasi altra app usando Socket.IO, ma puoi modificare i modelli di AngularJS in loro.

Il off () Metodo

Questo metodo rimuoverà uno o tutti i listener di eventi per un determinato evento. Questo ti aiuta a evitare perdite di memoria e comportamenti imprevisti. Immagina che tu stia usando ngRoute e allega pochi ascoltatori in ogni controller. Se l'utente naviga verso un'altra vista, il controller viene distrutto, ma il listener di eventi rimane collegato. Dopo alcune navigazioni avremo una perdita di memoria.

 off: funzione off (evento, callback)  

Dobbiamo solo verificare se il richiama è stato fornito e chiama socket.removeListener () o socket.removeAllListeners ():

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

Il emettere() Metodo

Questo è l'ultimo metodo di cui abbiamo bisogno. Come suggerisce il nome, questo metodo invierà dati al server:

 emit: function emit (evento, dati, callback)  

Poiché Socket.IO supporta i riconoscimenti, controlleremo se il file richiama era fornito. Se lo fosse, useremo lo stesso schema del sopra() metodo per chiamare il callback all'interno di $ Portata. Applicare $ ():

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

Se non c'è richiama possiamo solo chiamare socket.emit ():

  else socket.emit (event, data);  

uso

Per testare la libreria, creeremo un semplice modulo che invierà alcuni dati al server e visualizzerà la risposta. Tutto il codice JavaScript in questa sezione dovrebbe andare nel > tag nella del tuo documento e tutto l'HTML va nel suo .

Creazione del modulo

Per prima cosa dobbiamo creare un modulo per la nostra app:

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

Notare che 'Socket.io' nell'array, nel secondo parametro, dice ad AngularJS che questo modulo dipende dalla nostra libreria Socket.IO.

La funzione di configurazione

Poiché eseguiremo da un file HTML statico, dobbiamo specificare l'URL di connessione per Socket.IO. Possiamo farlo usando il config () metodo del modulo:

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

Come puoi vedere, il nostro $ socketProvider viene automaticamente iniettato da AngularJS.

Il controller

Il controller sarà responsabile di tutta la logica dell'app (l'applicazione è piccola, quindi ne abbiamo solo bisogno):

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

$ portata è un oggetto che contiene tutti i modelli del controller, è la base del binding di dati bidirezionale di AngularJS. $ presa è il nostro servizio Socket.IO.

In primo luogo, creeremo un ascoltatore per il 'eco' evento che verrà emesso dal nostro server di prova:

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

Mostreremo $ scope.serverResponse più tardi, in HTML, usando le espressioni di AngularJS.

Ora ci saranno anche due funzioni che invieranno i dati: uno che utilizza la base emettere() metodo e uno che usa emettere() con richiamata di conferma:

 $ 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 = "; ; ); 

Dobbiamo definirli come metodi di $ portata in modo che possiamo chiamarli dal ngClick direttiva in HTML.

L'HTML

È qui che risplende AngularJS: possiamo utilizzare l'HTML standard con alcuni attributi personalizzati per legare tutto insieme.

Iniziamo definendo il modulo principale usando un ngApp direttiva. Inserisci questo attributo nel tag del tuo documento:

 

Questo dice ad AngularJS che dovrebbe riavviare la tua app usando il esempio modulo.

Successivamente, possiamo creare un modulo di base per inviare dati al server:

 
Risposta del server: serverResponse
Risposta del server (ACK): serverResponseACK

Abbiamo usato alcuni attributi personalizzati e le direttive AngularJS lì:

  • ng-controllore - associa il controller specificato a questo elemento, consentendoti di utilizzare valori dal suo ambito
  • ng-model - crea un bind di dati bidirezionale tra l'elemento e la proprietà dell'ambito specificata (un modello), che consente di ottenere valori da questo elemento e di modificarlo all'interno del controller
  • ng-click - allega un clic listener di eventi che esegue un'espressione specificata (leggi di più sulle espressioni di AngularJS)

Le doppie parentesi graffe sono anche espressioni AngularJS, saranno valutate (non preoccuparti, non usare JavaScript eval ()) e il loro valore verrà inserito lì.

Se hai fatto tutto correttamente, dovresti essere in grado di inviare dati al server facendo clic sui pulsanti e vedere la risposta nel modo appropriato

tag.


In sintesi

In questa prima parte del tutorial, abbiamo creato la libreria Socket.IO per AngularJS che ci permetterà di sfruttare WebSockets nelle nostre app a pagina singola. Nella seconda parte, ti mostrerò come puoi migliorare la reattività delle tue app utilizzando questa combinazione.