Responsievere Single-Page-toepassingen met AngularJS & Socket.IO de bibliotheek maken

Er zijn geen HTML of HTTP gemaakt voor dynamische webtoepassingen. We baseren ons in feite op hacks, bovenop hacks om onze apps een responsieve gebruikersinterface te geven. AngularJS verwijdert enkele beperkingen van HTML, waardoor we gebruikersinterfacecode eenvoudiger kunnen maken en beheren. Socket.IO aan de andere kant helpt ons om gegevens van de server te verzenden, niet alleen wanneer de client daarom vraagt, maar ook wanneer de server dit moet doen. In dit artikel laat ik zien hoe je deze twee kunt combineren om de responsiviteit van je apps met één pagina te verbeteren.


Invoering

In het eerste deel van deze tutorial zullen we een herbruikbare AngularJS-service voor Socket.IO maken. Daarom herbruikbare een deel, dit zal een beetje lastiger zijn dan alleen maar gebruiken module.service () of module.factory (). Deze twee functies zijn slechts syntactische suiker bovenop het meer lage niveau module.provider () methode, die we zullen gebruiken om enkele configuratie-opties te bieden. Als u AngularJS nog nooit eerder hebt gebruikt, raad ik u ten zeerste aan om ten minste de officiële zelfstudie en enkele van de handleidingen hier op Tuts te volgen+.


Voorbereiding: het back-end

Voordat we beginnen met het schrijven van onze AngularJS-module, hebben we een eenvoudige back-end nodig om te testen. Als u al bekend bent met Socket.IO, kunt u gewoon naar het einde van dit gedeelte scrollen, de back-endbron kopiëren en doorgaan naar de volgende, zo niet - lees verder.

Vereiste modules

We zullen alleen nodig hebben socket.io. U kunt het rechtstreeks installeren met behulp van de NPM commando als volgt:

npm install socket.io 

Of maak een package.json bestand, zet deze regel in de afhankelijkheden sectie:

"socket.io": "0.9.x" 

En voer het uit npm installeren commando.

De Socket.IO-server maken

Omdat we geen ingewikkeld webraamwerk zoals Express nodig hebben, kunnen we de server maken met Socket.IO:

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

Dat is alles wat u nodig hebt om de Socket.IO-server in te stellen. Als u uw app start, ziet u vergelijkbare uitvoer in de console:

En je zou toegang moeten hebben tot de socket.io.js bestand in uw browser op http: // localhost: 8080 / socket.io / socket.io.js:

Omgaan met verbindingen

We zullen alle inkomende verbindingen in de verbinding gebeurtenis luisteraar van de io.sockets voorwerp:

io.sockets.on ('verbinding', functie (socket) ); 

De stopcontact attribuut doorgegeven aan de callback is de client die is verbonden en we kunnen naar gebeurtenissen erop luisteren.

Een standaardluisteraar

Nu voegen we een basisgebeurtenislistener toe in de bovenstaande callback. Het stuurt de ontvangen gegevens terug naar de client met behulp van de socket.emit () methode:

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

echo is de aangepaste evenementnaam die we later zullen gebruiken.

Een luisteraar met bevestiging

We zullen ook bevestigingen gebruiken in onze bibliotheek. Met deze functie kunt u een functie doorgeven als de derde parameter van de socket.emit () methode. Deze functie kan op de server worden aangeroepen om bepaalde gegevens terug naar de client te verzenden:

 socket.on ('echo-ack', functie (data, callback) callback (data);); 

Hiermee kunt u reageren op de client zonder dat deze naar gebeurtenissen hoeft te luisteren (wat handig is als u slechts enkele gegevens van de server wilt opvragen).

Nu is onze testback-end voltooid. De code zou er zo uit moeten zien (dit is de code die u moet kopiëren als u deze sectie overslaat):

var io = require ('socket.io') (8080); io.sockets.on ('verbinding', functie (socket) socket.on ('echo', functie (data) socket.emit ('echo', data);); socket.on ('echo-ack ', functie (data, callback) callback (data););); 

Start nu de app en laat hem draaien voordat je verder gaat met de rest van de tutorial.


Voorbereiding: de voorkant

We hebben natuurlijk wat HTML nodig om onze bibliotheek te testen. We moeten AngularJS opnemen, socket.io.js van onze back-end, onze hoekige socket.js bibliotheek en een eenvoudige AngularJS-controller om een ​​aantal tests uit te voeren. De controller is inline in de van het document om de workflow te vereenvoudigen:

           

Dit is alles wat we nu nodig hebben, we zullen later teruggaan naar de lege scripttag omdat we de bibliotheek nog niet hebben.


De AngularJS Socket.IO-bibliotheek maken

In deze sectie maken we de hoekige socket.js bibliotheek. Alle code moet in dit bestand worden geplaatst.

De module

Laten we beginnen met het maken van de module voor onze lib:

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

We hebben geen afhankelijkheden, dus de array in het tweede argument van angular.module () is leeg, maar verwijder het niet volledig, anders krijg je een $ Injector: nomod fout. Dit gebeurt omdat de vorm met één argument angular.module () haalt een verwijzing naar de reeds bestaande module op, in plaats van een nieuwe te maken.

De provider

Providers zijn een van de manieren om AngularJS-services te maken. De syntaxis is eenvoudig: het eerste argument is de naam van de service (niet de naam van de provider!) En de tweede is de constructorfunctie voor de provider:

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

Configuratie-opties

Om de bibliotheek herbruikbaar te maken, moeten we wijzigingen in de configuratie van Socket.IO toestaan. Laten we eerst twee variabelen definiëren die de URL voor de verbinding en het configuratieobject bevatten (code in deze stap gaat naar de $ SocketProvider () functie):

 var ioUrl = "; var ioConfig = ; 

Nu aangezien deze variabelen niet beschikbaar zijn buiten de $ SocketProvider () functie (ze zijn een soort van privaat), we moeten methoden (setters) maken om ze te veranderen. We kunnen ze natuurlijk gewoon maken openbaar zoals dit:

 this.ioUrl = "; this.ioConfig = ; 

Maar:

  1. We zouden moeten gebruiken Function.bind () later om toegang te krijgen tot de juiste context voor deze
  2. Als we setters gebruiken, kunnen we valideren om ervoor te zorgen dat de juiste waarden worden ingesteld - we willen niet plaatsen vals als de 'sluit time-out' keuze

Een volledige lijst met opties voor de Client van Socket.IO is te zien op hun GitHub-wiki. We zullen een setter maken voor elk van hen plus een voor de URL. Alle methoden lijken op elkaar, dus ik zal de code voor een van de methoden uitleggen en de rest hieronder zetten.

Laten we de eerste methode definiëren:

 this.setConnectionUrl = function setConnectionUrl (url)  

Er moet worden gecontroleerd welk type parameter is doorgegeven:

 if (typeof url == 'string')  

Als dit degene is die we verwachtten, stel dan de optie in:

 ioUrl = url; 

Zo niet, dan zou het moeten gooien Typefout:

  else gooi nieuwe TypeError ('url moet van het type string zijn'); ; 

Voor de rest kunnen we een helperfunctie maken om het droog te houden:

 functie setOption (naam, waarde, type) if (type van waarde! = type) gooi nieuwe TypeError ("'" + naam + "' moet van het type '" + type + "'" zijn);  ioConfig [naam] = waarde;  

Het gooit gewoon Typefout als het type verkeerd is, zet anders de waarde. Hier is de code voor de rest van de opties:

 this.setResource = function setResource (value) setOption ('resource', value, 'string'); ; this.setConnectTimeout = functie setConnectTimeout (waarde) setOption ('connect timeout', value, 'number'); ; this.setTryMultipleTransports = functiesetTryMultipleTransports (waarde) setOption ('try multiple transports', value, 'boolean'); ; this.setReconnect = functie setReconnect (waarde) setOption ('reconnect', value, 'boolean'); ; this.setReconnectionDelay = function setReconnectionDelay (value) setOption ('reconnection delay', value, 'number'); ; this.setReconnectionLimit = functie setReconnectionLimit (waarde) setOption ('reconnection limit', value, 'number'); ; this.setMaxReconnectionAttempts = function setMaxReconnectionAttempts (value) setOption ('max heraansluitpogingen', waarde, 'nummer'); ; this.setSyncDisconnectOnUnload = functie setSyncDisconnectOnUnload (waarde) setOption ('sync disconnect on unload', value, 'boolean'); ; this.setAutoConnect = function setAutoConnect (value) setOption ('auto connect', value, 'boolean'); ; this.setFlashPolicyPort = functie setFlashPolicyPort (waarde) setOption ('flash policy port', value, 'number'); this.setForceNewConnection = function setForceNewConnection (value) setOption ('force new connection', value, 'boolean'); ; 

Je zou het kunnen vervangen door een single SetOption () methode, maar het lijkt gemakkelijker om de naam van de optie in kameelgeval te typen, in plaats van het door te geven als een reeks met spaties.

De fabrieksfunctie

Met deze functie wordt het service-object gemaakt dat we later kunnen gebruiken (bijvoorbeeld in controllers). Laten we eerst de io() functie om verbinding te maken met de Socket.IO-server:

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

Merk op dat we de functie toewijzen aan de $ get eigenschap van het object gemaakt door de provider - dit is belangrijk omdat AngularJS die eigenschap gebruikt om het te noemen. We zetten ook $ rootScope als zijn parameter. Op dit punt kunnen we de afhankelijkheidsinjectie van AngularJS gebruiken voor toegang tot andere services. We zullen het gebruiken om wijzigingen door te geven aan alle modellen in Socket.IO-callbacks.

Nu moet de functie een object retourneren:

 terug ; ; 

We zullen alle methoden voor de service erin stoppen.

De op() Methode

Deze methode koppelt een gebeurtenislistener aan het socketobject, zodat we alle gegevens kunnen gebruiken die vanaf de server zijn verzonden:

 on: function on (event, callback)  

We zullen Socket.IO's gebruiken socket.on () om onze callback toe te voegen en deze in AngularJS's te bellen $ Scope. $ Van toepassing zijn () methode. Dit is erg belangrijk, omdat modellen alleen binnenin kunnen worden gewijzigd:

 socket.on (event, function ()  

Eerst moeten we de argumenten naar een tijdelijke variabele kopiëren, zodat we ze later kunnen gebruiken. Argumenten zijn natuurlijk alles dat de server naar ons heeft gestuurd:

 var args = argumenten; 

Vervolgens kunnen we onze callback bellen met Function.apply () om argumenten aan te geven:

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

Wanneer stopcontact's gebeurtenis-emitter roept de listener-functie aan die het gebruikt $ RootScope. $ Van toepassing zijn () om de callback genoemd als het tweede argument voor de .op() methode. Op deze manier kun je jouw evenement luisteraars zoals je zou voor elke andere app schrijven met behulp van Socket.IO, maar je kunt de AngularJS modellen in hen aanpassen.

De uit() Methode

Deze methode verwijdert een of alle gebeurtenislisteners voor een bepaalde gebeurtenis. Dit helpt u om geheugenlekken en onverwacht gedrag te voorkomen. Stel je voor dat je gebruikt ngRoute en je bevestigt weinig luisteraars in elke controller. Als de gebruiker naar een andere weergave navigeert, wordt uw controller vernietigd, maar blijft de gebeurtenislistener gehecht. Na enkele navigatiebeurten hebben we een geheugenlek.

 uit: functie uit (gebeurtenis, callback)  

We hoeven alleen maar te controleren of het Bel terug werd verstrekt en bel socket.removeListener () of socket.removeAllListeners ():

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

De zenden () Methode

Dit is de laatste methode die we nodig hebben. Zoals de naam al doet vermoeden, verzendt deze methode gegevens naar de server:

 uitstoten: functie-uitzending (gebeurtenis, data, callback)  

Aangezien Socket.IO bevestigingen ondersteunt, zullen we controleren of de Bel terug werd verstrekt. Als dat zo was, gebruiken we hetzelfde patroon als in de op() methode om callback binnen van te roepen $ Scope. $ Van toepassing zijn ():

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

Als er geen Bel terug we kunnen gewoon bellen socket.emit ():

  else socket.emit (event, data);  

Gebruik

Om de bibliotheek te testen, zullen we een eenvoudig formulier maken dat enkele gegevens naar de server zal sturen en het antwoord zal weergeven. Alle JavaScript-code in dit gedeelte moet in de > tag in de van uw document en alle HTML gaat in zijn .

De module maken

Eerst moeten we een module voor onze app maken:

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

Let erop dat 'Socket.io' in de array, in de tweede parameter, vertelt AngularJS dat deze module afhankelijk is van onze Socket.IO-bibliotheek.

De Config-functie

Omdat we een statisch HTML-bestand gebruiken, moeten we de verbindings-URL voor Socket.IO specificeren. We kunnen dit doen met behulp van de config () methode van de module:

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

Zoals je kunt zien, onze $ socketProvider wordt automatisch geïnjecteerd door AngularJS.

De controller

De controller is verantwoordelijk voor alle logica van de app (de applicatie is klein, dus we hebben er maar één nodig):

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

$ scope is een object dat alle modellen van de controller bevat, het is de basis van bi-directionele databinding van AngularJS. $ socket is onze Socket.IO-service.

Eerst zullen we een luisteraar maken voor de 'echo' evenement dat wordt uitgezonden door onze testserver:

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

We zullen weergeven $ scope.serverResponse later, in HTML, met behulp van de uitdrukkingen van AngularJS.

Nu zullen er ook twee functies zijn die de gegevens zullen verzenden - een die de basis gebruikt zenden () methode en één die gebruikt zenden () met callback voor bevestiging:

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

We moeten ze definiëren als methoden van $ scope zodat we ze kunnen bellen vanuit de ngClick richtlijn in HTML.

De HTML

Dit is waar AngularJS schittert: we kunnen standaard-HTML gebruiken met enkele aangepaste attributen om alles samen te binden.

Laten we beginnen met het definiëren van de hoofdmodule met behulp van een ngApp richtlijn. Plaats dit attribuut in de tag van uw document:

 

Dit vertelt AngularJS dat het uw app moet opstarten met behulp van de voorbeeld module.

Hierna kunnen we een basisformulier maken om gegevens naar de server te verzenden:

 
Serverreactie: serverResponse
Serverrespons (ACK): serverResponseACK

We hebben daar een aantal aangepaste kenmerken en AngularJS-richtlijnen gebruikt:

  • ng-controller - bindt de opgegeven controller aan dit element, zodat u waarden uit het bereik ervan kunt gebruiken
  • ng-model - maakt een bidirectionele gegevensbinding tussen het element en de opgegeven scopeigenschap (een model), waarmee u waarden uit dit element kunt ophalen en deze binnen de controller kunt wijzigen
  • ng-click - hecht een Klik gebeurtenislistener die een opgegeven expressie uitvoert (lees meer over AngularJS-expressies)

De dubbele accolades zijn ook AngularJS-expressies, ze worden geëvalueerd (maak je geen zorgen, geen JavaScript's eval ()) en hun waarde wordt daar ingevoegd.

Als je alles goed hebt gedaan, zou je gegevens naar de server moeten kunnen sturen door op de knoppen te klikken en het antwoord in de juiste volgorde te zien

labels.


Samengevat

In dit eerste deel van de zelfstudie hebben we de Socket.IO-bibliotheek voor AngularJS gemaakt die ons in staat stelt om te profiteren van WebSockets in onze apps met één pagina. In het tweede deel zal ik je laten zien hoe je de responsiviteit van je apps kunt verbeteren met behulp van deze combinatie.