Responsive Single-Page-Anwendungen mit AngularJS & Socket.IO Erstellen der Bibliothek

Weder HTML noch HTTP wurden für dynamische Webanwendungen erstellt. Wir setzen im Wesentlichen auf Hacks, zusätzlich zu Hacks, um unseren Apps eine ansprechende Benutzeroberfläche zu bieten. AngularJS hebt einige Einschränkungen aus HTML auf, sodass Benutzeroberflächencode einfacher erstellt und verwaltet werden kann. Socket.IO hingegen hilft uns beim Senden von Daten vom Server, nicht nur, wenn der Client dies anfordert, sondern auch, wenn der Server dies erfordert. In diesem Artikel werde ich Ihnen zeigen, wie Sie diese beiden kombinieren können, um die Reaktionsfähigkeit Ihrer einseitigen Apps zu verbessern.


Einführung

Im ersten Teil dieses Tutorials erstellen wir einen wiederverwendbaren AngularJS-Service für Socket.IO. Deswegen wiederverwendbar Teilweise wird dies ein wenig schwieriger sein als nur die Verwendung module.service () oder module.factory (). Bei diesen beiden Funktionen handelt es sich lediglich um syntaktischen Zucker neben dem eher niedrigen Niveau module.provider () Methode, die wir verwenden werden, um einige Konfigurationsoptionen bereitzustellen. Wenn Sie AngularJS noch nie verwendet haben, rate ich Ihnen dringend, mindestens das offizielle Tutorial und einige der Tutorials zu Tuts hier zu befolgen+.


Vorbereitung: Das Backend

Bevor wir anfangen, unser AngularJS-Modul zu schreiben, benötigen wir ein einfaches Back-End zum Testen. Wenn Sie bereits mit Socket.IO vertraut sind, können Sie einfach bis zum Ende dieses Abschnitts scrollen, die Back-End-Quelle kopieren und mit der nächsten fortfahren, falls nicht - lesen Sie weiter.

Erforderliche Module

Wir werden nur brauchen socket.io. Sie können es entweder direkt mit installieren npm Befehl wie folgt:

npm install socket.io 

Oder erstellen Sie eine package.json Datei, setzen Sie diese Zeile in die Abhängigkeiten Sektion:

"socket.io": "0.9.x" 

Und das ausführen npm installieren Befehl.

Socket.IO-Server erstellen

Da wir kein kompliziertes Web-Framework wie Express benötigen, können wir den Server mit Socket.IO erstellen:

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

Das ist alles, was Sie brauchen, um den Socket.IO-Server einzurichten. Wenn Sie Ihre App starten, sollten Sie eine ähnliche Ausgabe in der Konsole sehen:

Und Sie sollten auf das zugreifen können socket.io.js Datei in Ihrem Browser unter http: // localhost: 8080 / socket.io / socket.io.js:

Umgang mit Verbindungen

Wir werden alle eingehenden Verbindungen in der Verbindung Ereignis-Listener der Steckdosen Objekt:

io.sockets.on ('Verbindung', Funktion (Socket) ); 

Das Steckdose Das an den Rückruf übergebene Attribut ist der Client, der eine Verbindung hergestellt hat, und wir können Ereignisse darauf überwachen.

Ein einfacher Zuhörer

Jetzt fügen wir im obigen Rückruf einen grundlegenden Ereignis-Listener hinzu. Es sendet die empfangenen Daten mit dem Client an den Client zurück socket.emit () Methode:

 socket.on ('echo', Funktion (Daten) socket.emit ('echo', data);); 

Echo ist der benutzerdefinierte Ereignisname, den wir später verwenden werden.

Ein Zuhörer mit Bestätigung

Wir werden auch Bestätigungen in unserer Bibliothek verwenden. Mit dieser Funktion können Sie eine Funktion als dritten Parameter des übergeben socket.emit () Methode. Diese Funktion kann auf dem Server aufgerufen werden, um Daten an den Client zurückzusenden:

 socket.on ('echo-ack', Funktion (Daten, Rückruf) Rückruf (Daten);); 

Auf diese Weise können Sie auf den Client antworten, ohne dass er auf Ereignisse warten muss (was nützlich ist, wenn Sie nur einige Daten vom Server anfordern möchten)..

Nun ist unser Test-Backend abgeschlossen. Der Code sollte so aussehen (Dies ist der Code, den Sie kopieren sollten, wenn Sie diesen Abschnitt weglassen):

var io = erfordern ('socket.io') (8080); io.sockets.on ('Verbindung', Funktion (Socket) Socket.on ('Echo', Funktion (Daten) Socket.emit ('Echo', Daten);); Socket.on ('Echo-ack ', Funktion (Daten, Rückruf) Rückruf (Daten););); 

Sie sollten jetzt die App ausführen und laufen lassen, bevor Sie mit dem Rest des Lernprogramms fortfahren.


Vorbereitung: Das Frontend

Natürlich benötigen wir etwas HTML, um unsere Bibliothek zu testen. Wir müssen AngularJS einschließen, socket.io.js von unserem Backend, unserem Winkelsockel.js Bibliothek und einen grundlegenden AngularJS-Controller, um einige Tests auszuführen. Der Controller wird im eingeblendet des Dokuments, um den Workflow zu vereinfachen:

           

Das ist alles, was wir jetzt brauchen, wir werden später auf das leere Skript-Tag zurückkommen, da wir die Bibliothek noch nicht haben.


Erstellen der AngularJS Socket.IO-Bibliothek

In diesem Abschnitt erstellen wir die Winkelsockel.js Bibliothek. Der gesamte Code muss in diese Datei eingefügt werden.

Das Modul

Beginnen wir mit der Erstellung des Moduls für unsere Bibliothek:

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

Wir haben keine Abhängigkeiten, also das Array im zweiten Argument von angle.module () ist leer, aber nicht vollständig entfernt, sonst erhalten Sie eine $ Injektor: nomod Error. Dies geschieht aufgrund der Ein-Argument-Form von angle.module () Ruft einen Verweis auf das bereits vorhandene Modul ab, anstatt ein neues zu erstellen.

Der Provider

Anbieter sind eine der Möglichkeiten, AngularJS-Dienste zu erstellen. Die Syntax ist einfach: Das erste Argument ist der Name des Dienstes (nicht der Name des Anbieters!) Und das zweite Argument ist die Konstruktorfunktion für den Anbieter:

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

Einstellmöglichkeiten

Um die Bibliothek wieder verwendbar zu machen, müssen wir Änderungen in der Konfiguration von Socket.IO zulassen. Zunächst definieren wir zwei Variablen, die die URL für die Verbindung und das Konfigurationsobjekt enthalten (der Code in diesem Schritt geht an das $ socketProvider () Funktion):

 var ioUrl = "; var ioConfig = ; 

Nun, da diese Variablen außerhalb von nicht verfügbar sind $ socketProvider () Funktion (sie sind irgendwie Privatgelände), müssen wir Methoden (Setter) erstellen, um sie zu ändern. Wir könnten sie natürlich auch machen Öffentlichkeit so was:

 this.ioUrl = "; this.ioConfig = ; 

Aber:

  1. Wir müssten verwenden Function.bind () später auf den entsprechenden Kontext für zugreifen diese
  2. Wenn wir Setter verwenden, können wir überprüfen, ob die richtigen Werte festgelegt sind. Wir möchten nicht setzen falsch als die 'Verbindungs ​​Timeout' Möglichkeit

Eine vollständige Liste der Optionen für den Socket.IO-Client finden Sie im GitHub-Wiki. Wir erstellen für jeden einen Setter und einen für die URL. Alle Methoden sehen ähnlich aus, daher werde ich den Code für eine davon erläutern und den Rest unten angeben.

Definieren wir die erste Methode:

 this.setConnectionUrl = Funktion setConnectionUrl (URL)  

Es sollte den Typ des übergebenen Parameters überprüfen:

 if (typeof url == 'string')  

Wenn dies die erwartete ist, setzen Sie die Option:

 ioUrl = URL; 

Wenn nicht, sollte es werfen TypeError:

  else throw new TypeError ('url muss vom Typ string sein'); ; 

Für den Rest können wir eine Hilfsfunktion erstellen, um es trocken zu halten:

 function setOption (name, value, type) if (typeof value! = type) throw new TypeError ("'" + name + "' muss vom Typ '" + type + "'" sein);  ioConfig [Name] = Wert;  

Es wirft einfach TypeError Wenn der Typ falsch ist, wird sonst der Wert festgelegt. Hier ist der Code für die restlichen Optionen:

 this.setResource = Funktion setResource (value) setOption ('resource', value, 'string'); ; this.setConnectTimeout = Funktion setConnectTimeout (value) setOption ('Verbindungszeitlimit', Wert, 'Nummer'); ; this.setTryMultipleTransports = Funktion setTryMultipleTransports (value) setOption ('versuche mehrere Transporte', value, 'boolean'); ; this.setReconnect = Funktion setReconnect (value) setOption ('reconnect', value, 'boolean'); ; this.setReconnectionDelay = Funktion setReconnectionDelay (value) setOption ('Wiederherstellungsverzögerung', value, 'number'); ; this.setReconnectionLimit = Funktion setReconnectionLimit (value) setOption ('Wiederverbindungslimit', Wert, 'Nummer'); ; this.setMaxReconnectionAttetimes = Funktion setMaxReconnectionAttempt (value) setOption ('Versuche der max. Wiederverbindung', Wert, 'Nummer'); ; this.setSyncDisconnectOnUnload = Funktion setSyncDisconnectOnUnload (value) setOption ('sync disconnect on unload'), value, 'boolean'); ; this.setAutoConnect = Funktion setAutoConnect (value) setOption ('auto connect', value, 'boolean'); ; this.setFlashPolicyPort = Funktion setFlashPolicyPort (value) setOption ('flash policy port', value, 'number'); this.setForceNewConnection = Funktion setForceNewConnection (value) setOption ('neue Verbindung erzwingen', value, 'boolean'); ; 

Sie könnten es durch ein einzelnes ersetzen setOption () Methode, aber es scheint einfacher zu sein, den Namen der Option im Kamelfall einzugeben, anstatt ihn als Zeichenfolge mit Leerzeichen zu übergeben.

Die Werksfunktion

Diese Funktion erstellt das Serviceobjekt, das wir später verwenden können (z. B. in Controllern). Zuerst rufen wir die an io () Funktion zum Verbinden mit dem Socket.IO-Server:

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

Beachten Sie, dass wir die Funktion dem zuweisen $ bekommen Vom Provider erstellte Eigenschaft des Objekts. Dies ist wichtig, da AngularJS diese Eigenschaft zum Aufruf verwendet. Wir setzen auch $ rootScope als dessen Parameter. An diesem Punkt können wir die Abhängigkeitseingabe von AngularJS verwenden, um auf andere Dienste zuzugreifen. Wir werden es verwenden, um Änderungen an beliebigen Modellen in Socket.IO-Callbacks zu verbreiten.

Nun muss die Funktion ein Objekt zurückgeben:

 Rückkehr  ; ; 

Wir werden alle Methoden für den Dienst darin einsetzen.

Das auf() Methode

Diese Methode hängt einen Ereignis-Listener an das Socket-Objekt an, sodass wir alle vom Server gesendeten Daten verwenden können:

 ein: Funktion ein (Ereignis, Rückruf) 

Wir werden Socket.IOs verwenden socket.on () um unseren Rückruf anzuhängen und ihn in AngularJS zu nennen $ scope. $ apply () Methode. Dies ist sehr wichtig, da Modelle nur in ihr modifiziert werden können:

 socket.on (event, function ()  

Zuerst müssen wir die Argumente in eine temporäre Variable kopieren, damit wir sie später verwenden können. Argumente sind natürlich alles, was der Server an uns gesendet hat:

 var args = Argumente; 

Als nächstes können wir unseren Rückruf mit aufrufen Function.apply () Argumente übergeben:

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

Wann SteckdoseDer Ereignis-Emitter ruft die von ihm verwendete Listener-Funktion auf $ rootScope. $ apply () den Rückruf, der als zweites Argument bereitgestellt wird, an die .auf() Methode. Auf diese Weise können Sie Ihre Ereignis-Listener wie für jede andere App mit Socket.IO schreiben, aber Sie können die Modelle von AngularJS darin modifizieren.

Das aus() Methode

Diese Methode entfernt einen oder alle Ereignis-Listener für ein bestimmtes Ereignis. Dies hilft Ihnen, Speicherverluste und unerwartetes Verhalten zu vermeiden. Stellen Sie sich vor, Sie verwenden ngRoute und Sie hängen in jedem Controller nur wenige Zuhörer an. Wenn der Benutzer zu einer anderen Ansicht navigiert, wird Ihr Controller zerstört, der Ereignis-Listener bleibt jedoch verbunden. Nach ein paar Navigationen haben wir ein Gedächtnisleck.

 aus: Funktion aus (Ereignis, Rückruf) 

Wir müssen nur prüfen, ob die Ruf zurück wurde bereitgestellt und angerufen socket.removeListener () oder socket.removeAllListeners ():

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

Das emittieren() Methode

Dies ist die letzte Methode, die wir brauchen. Wie der Name vermuten lässt, sendet diese Methode Daten an den Server:

 emit: Funktion emit (Ereignis, Daten, Rückruf) 

Da Socket.IO Bestätigungen unterstützt, prüfen wir, ob Ruf zurück wurde bereitgestellt. Wenn ja, verwenden wir dasselbe Muster wie im auf() Rückrufmethode innerhalb von $ scope. $ apply ():

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

Wenn es keine gibt Ruf zurück Wir können einfach anrufen socket.emit ():

  else socket.emit (Ereignis, Daten);  

Verwendungszweck

Um die Bibliothek zu testen, erstellen wir ein einfaches Formular, das einige Daten an den Server sendet und die Antwort anzeigt. Der gesamte JavaScript-Code in diesem Abschnitt sollte in die > tag in der Ihres Dokuments und aller HTML-Dateien in seinem .

Das Modul erstellen

Zuerst müssen wir ein Modul für unsere App erstellen:

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

Beachte das 'socket.io' Im Array gibt AngularJS im zweiten Parameter an, dass dieses Modul von unserer Socket.IO-Bibliothek abhängt.

Die Config-Funktion

Da wir eine statische HTML-Datei verwenden, müssen Sie die Verbindungs-URL für Socket.IO angeben. Wir können dies mit der config () Methode des Moduls:

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

Wie Sie sehen können, unser $ socketProvider wird automatisch von AngularJS injiziert.

Der Controller

Der Controller ist für die gesamte Logik der App verantwortlich (die Anwendung ist klein, wir benötigen nur eine):

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

$ scope ist ein Objekt, das alle Modelle des Controllers enthält. Es ist die Basis der bidirektionalen Datenbindung von AngularJS. $ sockel ist unser Socket.IO Service.

Zuerst erstellen wir einen Listener für die 'Echo' Ereignis, das von unserem Testserver ausgegeben wird:

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

Wir werden zeigen $ scope.serverResponse später in HTML mit AngularJS-Ausdrücken.

Jetzt gibt es auch zwei Funktionen, die die Daten senden - eine mit der Basisfunktion emittieren() Methode und eine Verwendung emittieren() mit Quittungsrückruf:

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

Wir müssen sie als Methoden von definieren $ scope damit wir sie von der anrufen können ngClick Direktive in HTML.

Das HTML

AngularJS glänzt hier - wir können Standard-HTML mit einigen benutzerdefinierten Attributen verwenden, um alles miteinander zu verbinden.

Beginnen wir mit der Definition des Hauptmoduls mit einem ngApp Richtlinie. Platzieren Sie dieses Attribut in der Tag Ihres Dokuments:

 

Dies teilt AngularJS mit, dass es Ihre App mit dem Bootstrap starten soll Beispiel Modul.

Danach können wir ein Basisformular erstellen, um Daten an den Server zu senden:

 
Serverantwort: ServerResponse
Server Response (ACK): serverResponseACK

Wir haben dort einige benutzerdefinierte Attribute und AngularJS-Direktiven verwendet:

  • ng-Controller - bindet den angegebenen Controller an dieses Element, sodass Sie Werte aus seinem Bereich verwenden können
  • ng-modell - erstellt eine bidirektionale Datenbindung zwischen dem Element und der angegebenen scope-Eigenschaft (einem Modell), mit der Sie Werte aus diesem Element abrufen und innerhalb des Controllers ändern können
  • ng-klick - fügt ein klicken Ereignislistener, der einen angegebenen Ausdruck ausführt (lesen Sie mehr über AngularJS-Ausdrücke)

Die doppelten geschweiften Klammern sind ebenfalls AngularJS-Ausdrücke. Sie werden ausgewertet (keine Sorge, keine Verwendung von JavaScript) eval ()) und ihr Wert wird dort eingefügt.

Wenn Sie alles richtig gemacht haben, sollten Sie in der Lage sein, Daten an den Server zu senden, indem Sie auf die Schaltflächen klicken, und die Antwort in der entsprechenden Ansicht sehen

Stichworte.


In Summe

In diesem ersten Teil des Lernprogramms haben wir die Socket.IO-Bibliothek für AngularJS erstellt, mit der wir WebSockets in unseren einseitigen Apps nutzen können. Im zweiten Teil werde ich Ihnen zeigen, wie Sie mit dieser Kombination die Reaktionsfähigkeit Ihrer Apps verbessern können.