AngularJS kommt mit vielen „magischen“ Funktionen daher welche die Entwicklung und das Zusammenspiel vereinfachen, allem voran die Depedency Injection. Unsere Aufgaben als Entwickler um eine AngurJS Anwendung zu initialisieren beschränken sich auf die Einbindung der Bibliothek und die Deklaration einer ng-app Direktive. Aber was ist wenn wir unsere Anwendung mit Daten eines http-Services konfigurieren wollen bevor die Anwendung dem Benutzer zur Verfügung steht? An dieser Stelle steht AngularJS uns mit seinem strikten asynchronen Verhalten im Weg, bietet aber eine Ausweg: manuelles Bootstraping
Die normale Initialisierung einer AngularJS Anwendung findet ganz automatisch statt. Nachdem die HTML Seite geladen wurde (inklusive AngularJS Referenz) ermittelt Angular über die angular-app Direktive die zu ladende Anwendung, erzeugt den Injektions-Mechanimus und erzeugt/lädt alle benötigten Objekte und Module. Innerhalb dieses Prozesses werden auch die .config und .app Funktionen der Referenzierten Module zur Initialisierung geladen. Aber selbst in diesen Methoden ist es uns nicht möglich auf die Abarbeitung von asynchronen Anfragen zu warten (um zum Beispiel eine Konfiguration über http zu laden). Um ein solches Szenario dennoch zu realsieren bestehen mehrer Möglichkeiten
- einen synchron xhr-Request stellen ohne http-Service (xmlhttp.open(method, url, false))
- syncrone Anfragen im main Thread sind deprecated
- ui-router mit einem Mehrstufigem Template dessen Root-State mittels „resolve“ die benötigten Daten lädt
- manuelle Bootstraping
Um ein manuelles Boostraping unserer AngularJS Anwendung zu realisieren müssen wir im ersten Schritt dafür Sorge tragen das AngularJS mit seinem Automatismus uns nicht zuvorkommt. Aus diesem Grund entfernen wir die ng-app Direktive aus unserer HTML-Seite:
Vorher: < html ng-app="AngularBootstraping"> Nachher: < html>
Damit führt AngularJS keinerlei Initialisierung durch und es liegt an uns diese, nach Fertigstellung unserer Konfiguration, aus zu führen. Ausgehend davon das unser Angular Modul den Namen: „AngularBootstraping“ trägt könnte eine solche Methode so aussehen:
(function () { "use strict"; // Erzeugen einer Injector Instanz // dazu übergeben wir die von uns benötigten Module // inklusive 'ng' welches uns diverse Angular Objekte zur Verfügung stellt var initInjector = angular.injector(["ng", "AngularJSi18n.globalize.services", "AngularBootstraping.initialize"]); // den Injektor nutzen um manuelle Dependency Injection durch zu führen // und asyconre Initialisierungen durchführen, z.B: // 1. Initialisierung von Globalize über eine Factory var $window = initInjector.get("$window"); var lang = $window.navigator.language || $window.navigator.userLanguage; var GlobalizeLanguageLoader = initInjector.get("GlobalizeLanguageLoader"); // 2. KonfigurationsObjekt über Rest Schnittstelle laden var configPromise = initInjector.get("InitFactory").getConfig(); var globalizePromise = GlobalizeLanguageLoader.loadLanguage(lang); // auf alle asychronen Requests warten: var $q = initInjector.get("$q"); $q.all([configPromise, globalizePromise]).then(function(result){ // die Ergebnisse unserer Initialisierung legen wir als Konstanten ab: angular.module("AngularBootstraping").constant("$config", result[0]); angular.module("AngularBootstraping").constant("$globalize", result[1]); // erst jetzt lassen wir AngularJS seine Initialisierung durchführen angular.bootstrap(document, ["AngularBootstraping"]); }); })();
Damit stellen wir sicher dass alle unsere Aufgaben zur Initialisierung beim eigentlichen Programmstart bereits abgeschlossen sind. Wichtig: in einer Produktiven Umgebung sollte eine stabile Fehlerbehandlung implementiert werden, wenn einer der Schritte innerhalb der Initialisierung fehlschlägt (z.B. Rest-Services stehen nicht zur Verfügung) findet keine Anzeige der Anwendung statt.
Asynchrone Requests sind ein wichtiger Bestandteil von AngularJS Anwendungen die ein anwenderfreundliches Handling sicherstellt. Wir sollten nur in unausweichlichen Situationen auf diese Initialisierung zurückgreifen da sie den Start der Anwendung entsprechend verzögert ohne das der Anwender über die aktiven Lade-Vorgänge informiert wird. Dennoch gibt es durchaus solche Situationen für die AngularJS, wie zu sehen, eine passende Lösung parat hält.