AngularJS Patterns: Resolver als eigenständige Klasse

Als ich die ersten Schritte mit AngularJS gegangen bin, war ich total geflasht. Die Entwickler von AngularJS haben's einfach drauf, die Konzepte stimmen, alles läuft wie geschmiert.

An einer Stelle bin ich aber ins Stocken geraten, teils weil dies nicht gut dokumentiert war und teils, weil das Konzept den Entwickler nicht gut unterstützt. Es geht um die den $routeProvider und die resolve-Eigenschaft.

Den Nutzen von resolve stellt John Lindquist in einem Youtube-Video recht gut dar. Mit resolve lässt sich bewirken, dass ein Seitenwechsel in AngularJS erst dann stattfindet, wenn bestimmte Daten geladen sind. Ohne resolve müsste das initiale Laden von Daten durch einen Controller gesteuert werden und es kommt zu dem unschönen Effekt, das wichtige Bestandteile der Seite initial noch nicht dargestellt und erst verzögert eingeblendet werden können.

Was mich an den Codebeispielen von John, aber auch von anderen Entwicklern stört, ist die Platzierung des resolve-Codes. In obigem Video wird der resolve-Code an zentraler Stelle in einer app.js gehalten. Das halte ich nicht für optimal, wenn eine Anwendung viele Seiten mit vielen resolve-Funktionen hat. Auch die Variante, resolve als statische Funktion eines Controllers zu implementieren, wie ich es in manchen Beispielen gesehen habe, halte ich für fehlplatziert.

Eigene Resolver-Klassen

Stattdessen habe ich mich entschieden, jedes resolve über eine eigene Resolver-Klasse zu implementieren. Dieses Vorgehen habe ich im Rahmen der Entwicklung von monkkee entwickelt, wo im Rahmen von resolve nicht nur Daten geladen werden, sondern diese weiter aufbereitet (u.a. entschlüsselt) werden.

Eigene Resolver-Klassen führen zu einer klaren Separation of Concerns zwischen Resolvern und Controllern sowie zwischen Resolvern und zentraler Initialisierung des Anwendungsmoduls.

Ich habe ein einfaches Beispiel als JSFiddle aufgebaut: http://jsfiddle.net/bjoerne/GTJ78/. Der Javascript-Teil des Beispiels sieht wie folgt aus:

ExampleResolver = function($q) {
    deferred = $q.defer()
    deferred.resolve('Superhero');
    return deferred.promise;
};  
ExampleResolver.resolve = { name: ExampleResolver };

ExampleCtrl = function($scope, name) {
    $scope.name = name;
};

myApp = angular.module('myApp', ['ngRoute']);
myApp.config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: 'index.html',
        controller: ExampleCtrl,
        resolve: ExampleResolver.resolve
    });
}]);
angular.bootstrap(document, ['myApp']);

Wenn der $routeProvider konfiguriert wird, wird ExampleResolver.resolve als Wert für resolve angegeben. Am Ende jeder Resolver-Deklaration wird diese statische Eigenschaft gesetzt, hier  auf { name: ExampleResolver }. AngularJS erzeugt dann eine ExampleResolver-Instanz, die ein promise zurückgibt. Sobald dieses aufgelöst ist, wird der Controller erzeugt und bekommt den aufgelösten name injiziert.

Im Code von monkkee konnte ich mit diesem Ansatz den teilweise komplexen resolve-Prozess als eigenständigen Teil behandeln. Jeder Resolver befindet sich in einer eigenen Datei. Alle Resolver werden mit Jasmine-Specs getestet.

 

bjoerne_com_bjoern_weinbrenner_softwareentwickler_icon_leistungen_02

Lust auf mehr? Als Experte für AngularJS und individuelle JavaScript-Entwicklung kann ich Sie in Ihren Projekten unterstützen. Melden Sie sich gerne bei mir.

2 Kommentare

  1. Thomas 18. Februar 2014 um 17:25 Uhr- Antworten

    Hi, I really like your approach to modularize everything as much as possible in classes. This make code readability and maintenance so much easier - great work. However, it seem that the resolver example does not seem to work any more with latest angularjs 1.2.1 - at least the fiddle doesn't. Any idea where this may come from?

    • Björn Weinbrenner 26. Februar 2014 um 11:19 Uhr- Antworten

      Thanks for the hint. It comes from a change in AngularJS. The route handling is extracted into an own module ngRoute. I changed the JSFiddle mentioned above and it works again.

Hinterlassen Sie einen Kommentar