Gleich nach der ersten Berührung mit AngularJS werden für alltägliche Entwickler-Probleme einfache aber mächtige Lösungen geboten, beispielsweise „dependency injection“, „two-way data binding“ und weitere zauberhafte Dinge …

Eine lustige aber durchaus wahre Illustration zu der Lernkurve in die wunderbare Angular-Welt stammt von John Papa (@codeOnTheBeach):

Learning AngularJS

Es gibt eine simple Erklärung für diese Fluktuation: die Basisfunktionen sind herrlich einfach zu nutzen um schnelle Erfolge zu erzielen. Aber für komplexere Anwendungen muss man oft tief ins Innere des Frameworks eintauchen – das kostet Zeit, ist mitunter mühsam und schmälert damit die Euphorie …

Überblick

Hier ein kurzer Über- oder Einblick zur $resource (auch ngResource) Komponente – einer von den meist missverstandenen Services von Angular.

$resource ist ein atomares Element jedes REST API Designs – alles was „einen Namen“ hat, kann ein $resource sein: ein Dokument, ein Image, ein Service, eine Sammlung von anderen Ressourcen – oder auch nur ein einfaches Objekt (Person, etc). $resource ist also ein extrem vielfältiger API-Service – genau genommen ein Factory der auf dem $http Service aufbaut, der außer der Interaktion mit einem RESTful backend noch folgende Aufgaben erfüllen kann:

  • eigene request Methoden
  • default parameter values
  • eigene Serialisierungslogik
  • Cache Unterstützung
  • und noch vieles mehr …

Anstatt einer feingranularen und in die Tiefe gehenden Implementierung trifft $resource viele Annahmen, wie Webservice Endpoints aussehen „müssten“ und konstruiert mit minimalen Informationen vom Entwickler die Objekte, mit denen man möglichst vielfältig die unterschiedlichen requests bedienen kann.

Die Abhängigkeiten von $resource können etwa so dargestellt werden:

Einsatz

Da $resource nicht mit dem Angular Core gebündelt ist, muss er explizit geladen werden (das Script angular-resource.js ist gehostet unter anderem auf der Google-Webseite und bei angular.org):

angular.module('mainApp',['ngResource']); // mainApp ist unser Hauptmodul

Bei dem REST call wird auf der Gegenseite ein ganz klassisches RESTful backend erwartet, so sollte die API für create, update, retreive und delete folgendermaßen aussehen:

POST   /entries
POST   /entries /:id # Put ist hier etwas gewöhnungsbedürftig, kann aber benutzt werden
DELETE /entries /:id
GET    /entries
GET    /entries /:id

Mit diesem Hintergrundwissen können wir unser Modell (NICHT View Modell!) erstellen:

var Entry = $resource('/entries/:id');

Mit dieser einzigen Zeile haben wir einen Konstruktor für unser Modell kreiert. Jede Instanz dieses Modells verfügt über Methoden, die für die gewöhnlichen CRUD Operationen verwendet werden können:

get()
query()
save()
remove()
delete()

Es ist dringend empfehlenswert den erstellten Konstruktor in einen Service zu packen, unter anderem damit er global für die ganze Anwendung verfügbar, einfacher erweiterbar und besser testbar ist:

module.factory('Entry', function ($resource) {  
    return $resource('/entries/:id');
});

Und damit kann die erstellte Instanz wie folgt benutzt werden:

resourceDemo.controller('MainCtrl', function ($scope, Entry) {  
    $scope.entries = Entry.query();
});

Funktionsweise

Wer denkt, dass die Operation query synchron ist, liegt falsch – das wäre eine schlechte Idee …

Die Operation ist natürlich asynchron: die Antwort vom Backend ist erst mal nur ein $promise, eine Referenz auf ein leeres Array – es kann erst manipuliert werden, sobald der Request erfolgreich beendet wird.

// The WRONG way!
var name = $resource.get("/api/my/name");

So kommt man leider nicht an das erwünschte Ergebnis – der richtige Weg ist:

var promise = $http.get("/api/my/name");   
promise.success(function(name) {     
    console.log("Your name is: " + name);
});
promise.error(function(response, status) {     
    console.log("The request failed.);
});

success() und error() sind spezielle Funktionen, mit then() erfolgt der allgemeiner Zugriff auf ein promise.

Fazit

Wie wir gesehen haben: mit $resource spart man jede Menge Arbeit, sofern im Backend eine nicht allzu komplexe API zur Verfügung gestellt wird.

In diesem Fall hat man die Wahl alles selbst zu implementieren (wie es in vielen klassischen Architekturen der Fall ist) oder die nicht konformen Methoden von $resource zu überschreiben.