Mer Responsive One-Page-applikationer med AngularJS & Socket.IO Skapa biblioteket

Varken HTML eller HTTP skapades för dynamiska webbapplikationer. Vi är i grund och botten beroende av hack, ovanpå hackar för att ge våra appar ett användarvänligt användargränssnitt. AngularJS tar bort några begränsningar från HTML, så att vi enkelt kan skapa och hantera användargränssnittskoden. Socket.IO hjälper oss däremot att skicka data från servern inte bara när kunden begär det, men också när servern behöver. I den här artikeln ska jag visa dig hur du kombinerar dessa två, för att förbättra responsen på dina enkelsidiga appar.


Introduktion

I den första delen av denna handledning skapar vi en återanvändbar AngularJS-tjänst för Socket.IO. På grund av det återanvändbar del kommer det att bli lite svårare än att bara använda module.service () eller module.factory (). Dessa två funktioner är bara syntaktiskt socker på toppen av den mer låga nivån module.provider () metod som vi kommer att använda för att ge några konfigurationsalternativ. Om du aldrig använt AngularJS tidigare rekommenderar jag starkt att du åtminstone följer den officiella handledningen och några av handledningarna här på Tuts+.


Förberedelse: Back-End

Innan vi börjar skriva vår AngularJS-modul behöver vi ett enkelt back-end för testning. Om du redan är bekant med Socket.IO kan du bara rulla ner till slutet av det här avsnittet, kopiera backend-källan och fortsätta till nästa, om inte - läs vidare.

Obligatoriska moduler

Vi behöver bara socket.io. Du kan antingen installera den direkt med hjälp av npm kommando så här:

npm installera socket.io 

Eller skapa en package.json fil, sätt den här raden i beroenden sektion:

"socket.io": "0.9.x" 

Och exekvera npm installera kommando.

Skapa Socket.IO-servern

Eftersom vi inte behöver någon komplicerad webbram som Express kan vi skapa servern med Socket.IO:

var io = kräver ('socket.io') (8080); 

Det är allt du behöver för att konfigurera Socket.IO-servern. Om du startar din app bör du se liknande resultat i konsolen:

Och du ska kunna komma åt socket.io.js filen i din webbläsare på http: // localhost: 8080 / socket.io / socket.io.js:

Hantering av anslutningar

Vi hanterar alla inkommande anslutningar i förbindelse händelse lyssnare av io.sockets objekt:

io.sockets.on ('connection', funktion (uttag) ); 

De uttag attributet som skickas till återuppringningen är den klient som anslutits och vi kan lyssna på händelser på den.

En grundläggande lyssnare

Nu lägger vi till en bashändelselyttare i återuppringningen ovan. Den kommer att skicka de mottagna dataen, tillbaka till klienten med hjälp av socket.emit () metod:

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

eko är det anpassade evenemangsnamnet som vi kommer att använda senare.

En lyssnare med bekräftelse

Vi kommer också att använda bekräftelser i vårt bibliotek. Med den här funktionen kan du skicka en funktion som den tredje parametern för socket.emit () metod. Denna funktion kan kallas på servern för att skicka några data tillbaka till klienten:

 socket.on ("echo-ack", funktion (data, återuppringning) callback (data);); 

Detta gör att du kan svara på klienten utan att behöva lyssna på några händelser (vilket är användbart om du bara vill begära vissa data från servern).

Nu är vårt testback-end färdigt. Koden ska se ut så här (Det här är koden du ska kopiera om du släppte bort det här avsnittet):

var io = kräver ('socket.io') (8080); io.sockets.on ("anslutning", funktion (socket) socket.on ("echo", funktion (data) socket.emit ("echo", data);); socket.on ', funktion (data, återuppringning) callback (data););); 

Du ska nu köra appen och lämna den innan du fortsätter med resten av handledningen.


Förberedelse: Framsidan

Vi behöver naturligtvis lite HTML för att testa vårt bibliotek. Vi måste inkludera AngularJS, socket.io.js från vårt back-end, vår vinkel-socket.js bibliotek och en grundläggande AngularJS-kontroller för att köra några test. Kontrollern kommer att vara inline i av dokumentet för att förenkla arbetsflödet:

           

Det här är allt vi behöver för nu, vi kommer tillbaka till den tomma skripttaggen senare eftersom vi inte har biblioteket ännu.


Skapa AngularJS Socket.IO Library

I det här avsnittet kommer vi att skapa vinkel-socket.js bibliotek. All kod måste sättas in i den här filen.

Modulen

Låt oss börja med att skapa modulen för vår lib:

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

Vi har inga beroenden, så arrayen i det andra argumentet av angular.module () är tom, men ta inte bort det helt eller du får en $ Injektor: nomod fel. Detta händer på grund av att enargumentformen av angular.module () hämtar en referens till den redan existerande modulen istället för att skapa en ny.

Leverantören

Leverantörer är ett sätt att skapa AngularJS-tjänster. Syntaxen är enkel: Det första argumentet är namnet på tjänsten (inte leverantörens namn!) Och den andra är konstruktörfunktionen för leverantören:

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

Konfigurationsalternativ

För att göra biblioteket återanvändbart måste vi tillåta ändringar i Socket.IOs konfiguration. Låt oss först definiera två variabler som kommer att hålla URL-adressen för anslutningen och konfigurationsobjektet (koden i det här steget går till $ SocketProvider () fungera):

 var ioUrl = "; var ioConfig = ; 

Nu eftersom dessa variabler inte är tillgängliga utanför $ SocketProvider () funktion (de är typiska privat), måste vi skapa metoder (setters) för att ändra dem. Vi kunde naturligtvis bara göra dem offentlig så här:

 this.ioUrl = "; this.ioConfig = ; 

Men:

  1. Vi skulle behöva använda Function.bind () senare för att komma åt det lämpliga sammanhanget för detta
  2. Om vi ​​använder setter, kan vi validera för att se till att rätt värden är inställda - vi vill inte sätta falsk som den "ansluta timeout" alternativ

En fullständig lista över alternativ för Socket.IOs Client kan ses på deras GitHub wiki. Vi kommer att skapa en setter för var och en av dem plus en för webbadressen. Alla metoder ser likadana ut, så jag kommer att förklara koden för en av dem och lägga resten nedan.

Låt oss definiera den första metoden:

 this.setConnectionUrl = funktion setConnectionUrl (url)  

Det ska kontrollera vilken typ av parameter som passerat i:

 om (typ av url == 'sträng')  

Om det är det vi förväntade oss, sätt alternativet:

 ioUrl = url; 

Om inte, bör det kasta Skrivfel:

  annars släng ny TypeError ('url måste vara av typen sträng'); ; 

För resten av dem kan vi skapa en hjälpfunktion för att hålla den torr:

 funktion setOption (namn, värde, typ) if (typof value! = typ) kasta ny TypeError ("'" + namn + "" måste vara av typen "" + typ + "" ");  ioConfig [namn] = värde;  

Det kastar bara Skrivfel Om typen är fel anger du annars värdet. Här är koden för resten av alternativen:

 this.setResource = funktionsuppsättningResource (värde) setOption ('resource', value, 'string'); ; this.setConnectTimeout = funktion setConnectTimeout (värde) setOption ('connect timeout', värde, 'number'); ; this.setTryMultipleTransports = funktion setTryMultipleTransporter (värde) setOption ("försök flera transporter", värde, "boolean"); ; this.setReconnect = function setReconnect (value) setOption ('reconnect', värde, 'boolean'); ; this.setReconnectionDelay = function setReconnectionDelay (value) setOption ('återkopplingsfördröjning', värde, 'nummer'); ; this.setReconnectionLimit = funktion setReconnectionLimit (värde) setOption ('reconnection limit', värde, 'number'); ; this.setMaxReconnectionAttempts = funktionsuppsättningMaxReconnectionAttempts (value) setOption ("max återkopplingsförsök", värde, "nummer"); ; this.setSyncDisconnectOnUnload = function setSyncDisconnectOnUnload (value) setOption ('synkronisera frånkoppling', värde, 'boolean'); ; this.setAutoConnect = funktion setAutoConnect (värde) setOption ("auto connect", värde, "boolean"); ; this.setFlashPolicyPort = funktion setFlashPolicyPort (värde) setOption ('flash policy port', värde, 'number'); this.setForceNewConnection = funktion setForceNewConnection (värde) setOption ('force new connection', värde, 'boolean'); ; 

Du kan ersätta den med en enda setoption () metod, men det verkar lättare att skriva alternativets namn i kamelfall, snarare än att överföra det som en sträng med mellanslag.

Fabriksfunktionen

Den här funktionen skapar det serviceobjekt som vi kan använda senare (till exempel i kontroller). Låt oss först ringa io () funktion för att ansluta till Socket.IO-servern:

 detta. $ get = funktion $ socketFactory ($ rootScope) var socket = io (ioUrl, ioConfig); 

Observera att vi tilldelar funktionen till $ get egenskapen hos objektet skapat av leverantören - det här är viktigt eftersom AngularJS använder den egenskapen för att ringa den. Vi lägger också $ rootScope som dess parameter. Vid denna tidpunkt kan vi använda AngularJS beroendeinsprutning för att komma åt andra tjänster. Vi kommer att använda den för att sprida ändringar till alla modeller i Socket.IO callbacks.

Nu behöver funktionen returnera ett objekt:

 lämna tillbaka  ; ; 

Vi kommer att lägga alla metoder för tjänsten i den.

De på() Metod

Den här metoden kommer att bifoga en händelseloggare till socketobjektet, så vi kan använda alla data som skickas från servern:

 på: funktion på (händelse, återuppringning)  

Vi kommer att använda Socket.IO: s socket.on () att bifoga vår återkallelse och ringa den i AngularJS $ Omfattning. $ Gäller () metod. Detta är väldigt viktigt, eftersom modeller bara kan ändras inuti det:

 socket.on (händelse, funktion ()  

Först måste vi kopiera argumenten till en temporär variabel så att vi kan använda dem senare. Argument är givetvis allt som servern skickade till oss:

 var args = argument; 

Därefter kan vi ringa vår återuppringning med Function.apply () att skicka argument till det:

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

När uttagsändningsemitter kallar lyssnarfunktionen som den använder $ RootScope. $ Gäller () att ringa återkallningen som den andra argumentet till .på() metod. På så sätt kan du skriva dina händelselöser som du skulle för någon annan app med Socket.IO, men du kan ändra AngularJS modeller i dem.

De av() Metod

Den här metoden tar bort en eller alla händelselyttare för en viss händelse. Detta hjälper dig att undvika minnesläckor och oväntat beteende. Tänk dig att du använder ngRoute och du bifogar några lyssnare i varje kontroller. Om användaren navigerar till en annan vy förstörs din styrenhet, men händelseläsenaren förblir fäst. Efter några navigeringar har vi en minnesläcka.

 av: funktion av (händelse, återuppringning)  

Vi behöver bara kolla om ring tillbaka tillhandahölls och ringde socket.removeListener () eller socket.removeAllListeners ():

 om (typ av återuppringning == 'funktion') socket.removeListener (händelse, återuppringning);  annat socket.removeAllListeners (event); , 

De avge() Metod

Det här är den sista metoden som vi behöver. Som namnet antyder kommer denna metod att skicka data till servern:

 avge: funktion emit (händelse, data, återuppringning)  

Eftersom Socket.IO stöder bekräftelser, kommer vi att kontrollera om ring tillbaka tillhandahölls. Om det var så kommer vi att använda samma mönster som i på() metod för att ringa uppringningen inuti $ Omfattning. $ Gäller ():

 om (typ av återuppringning == 'funktion') socket.emit (händelse, data, funktion () var args = arguments; $ rootScope. $ apply (funktion () callback.apply (socket, args);); ); 

Om det finns nej ring tillbaka vi kan bara ringa socket.emit ():

  else socket.emit (händelse, data);  

Användande

För att testa biblioteket skapar vi en enkel blankett som skickar vissa data till servern och visar svaret. Alla JavaScript-koden i det här avsnittet ska gå i > tagga i av ditt dokument och all HTML går i dess .

Skapa modulen

Först måste vi skapa en modul för vår app:

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

Lägg märke till att 'Socket.io' I matrisen, i den andra parametern, berättar AngularJS att denna modul beror på vårt Socket.IO-bibliotek.

Konfig-funktionen

Eftersom vi kommer att springa från en statisk HTML-fil måste vi ange anslutningsadressen för Socket.IO. Vi kan göra detta med hjälp av config () metod för modulen:

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

Som du kan se, vårt $ socketProvider injiceras automatiskt av AngularJS.

Controller

Kontrollern ansvarar för all appens logik (applikationen är liten, så vi behöver bara en):

app.controller ('Ctrl', funktion Ctrl ($ scope, $ sockel)  

$ omfattning är ett objekt som håller alla regulatorns modeller, det är basen för AngularJS bi-riktiga databindande. $ socket är vår Socket.IO tjänst.

Först ska vi skapa en lyssnare för 'eko' händelse som kommer att emitteras av vår testserver:

 $ socket.on ("echo", funktion (data) $ scope.serverResponse = data;); 

Vi kommer att visa $ scope.serverResponse senare i HTML, med AngularJS uttryck.

Nu kommer det också att finnas två funktioner som skickar data - en med den grundläggande avge() metod och en som använder avge() med bekräftelse återuppringning:

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

Vi måste definiera dem som metoder för $ omfattning så att vi kan kalla dem från ngClick direktivet i HTML.

HTML

Det här är där AngularJS lyser - vi kan använda standard HTML med några anpassade attribut för att binda allt ihop.

Låt oss börja med att definiera huvudmodulen med hjälp av en ngApp direktiv. Placera detta attribut i tagg av ditt dokument:

 

Detta berättar AngularJS att det ska starta upp din app med hjälp av exempel modul.

Därefter kan vi skapa en grundläggande form för att skicka data till servern:

 
Serverrespons: serverResponse
Serverrespons (ACK): serverResponseACK

Vi använde några anpassade attribut och AngularJS-direktiv där:

  • ng-regulator - binder den angivna kontrollenheten till det här elementet, så att du kan använda värden från dess omfattning
  • ng-modell - skapar en dubbelriktad databindning mellan elementet och den angivna räckviddsegenskapen (en modell), som låter dig få värden från det här elementet såväl som att ändra det inuti kontrollenheten
  • ng-klick - fäster a klick händelse lyssnare som kör ett visst uttryck (läs mer på AngularJS uttryck)

De dubbla lockiga axlarna är också AngularJS uttryck, de kommer att utvärderas (oroa dig inte, använd inte JavaScript eval ()) och deras värde kommer att införas där inne.

Om du har gjort allt korrekt borde du kunna skicka data till servern genom att klicka på knapparna och se svaret i det aktuella

taggar.


Sammanfattningsvis

I den här första delen av handledningen har vi skapat Socket.IO-biblioteket för AngularJS som gör att vi kan utnyttja WebSockets i våra enkelsidiga appar. I den andra delen kommer jag att visa dig hur du kan förbättra responsen hos dina appar med den här kombinationen.