In meinem aktuellen Angular.js Training wurde REST Service per CRUD Operationen eingebunden. Rein aus akademischen Gründen wurde nach refactoring auf einen Angular Service bzw Factory umgestellt. Zusätzlich kommt die $http Abstraktion $resource zum Einsatz.
Das User Interface sieht so aus und erlaubt das Ändern des Namens, löschen, einen neuen Datensatz einfügen und natürlich eine Selektion.
Es wird die JavaScript Datei angular-resource.js benötigt um $resource nutzen zu können (auch ein Service). Die Url entspricht dabei der URL des ASP.NET Web Api Endpunktes. Teilweise werden die Daten als JSON im Body erwartet und teilweise auch/oder die ID in der URL (DELETE, UPDATE)
Um einen Angular.JS Service zu implementieren kommt folgender JavaScript Code zum Einsatz.
1: angular.module("App")
2: .service("webapi", function ($resource) {
3: var that = this;
4: that.svc = $resource("/api/teilnehmer/:ID");
5:
6: });
Die Variante als Angular Factory
1: angular.module("App")
2: .factory("webapiFactory", function ($resource) {
3: return $resource("/api/teilnehmer/:id");
4: });
Im Angular Controller (hier mit der ControllerAS Syntax) wird dann wesentlich weniger Code benötigt. Zunächst wird der Service oder die Factory per Dependency Injection injiziert.
1: angular.module("App")
2: .controller("teilnehmerController", function ( $scope, webapi, webapiFactory) {
3: var vm = this;
4: vm.teilnehmer = [];
5:
Die Daten werden als Liste geladen, per query Funktion. Mit Save wird ein neuer Teilnehmer angelegt. Wenn man sich in der Objekt Struktur und Benennung auf der Client Seite an die Klassen auf der Server Seite 1:1 hält, reicht eine Übergabe des Objektes als Parameter.
1: vm.teilnehmer = webapi.svc.query();
2:
3: vm.add = function (p) {
4: webapi.svc.save(p);
5:
6: }
7:
Es wird auf der Leitung ein http Post gesendet.
Wenden wird uns dem SQL Update zu. Zunächst steht dazu eine Funktion delete auf $resource bereit. Dieses erwartet aber ein JavaScript Objekt als Parameter. Die Varianten in Zeile 8 und Zeile 9 funktionieren. Im Kommentar steht die gesendete URL. Für mich spannend ist, das ASP.NET Web Api auch die Variante mit dem QueryString akzeptiert. Warum Zeile 11 dann doch anders aussieht klärt sich gleich.
1: vm.delete = function (Id) {
2: for (var i = 0; i < vm.teilnehmer.length ; i++) {
3: if (vm.teilnehmer[i].Id == Id) {
4: index = i;
5: break;
6: }
7: }
8:
9: // webapi.svc.delete({ ID: Id }); //http://localhost:40527/api/teilnehmer/37
10: // webapi.svc.delete({ Id }); //http://localhost:40527/api/teilnehmer?Id=38
11: webapi.svc.delete({ Id: Id });
12: vm.teilnehmer.splice(index, 1);
13: }
Ein REST Update wird fast überall per HTTP PUT ausgelöst. Nur die Angular Welt funktioniert anders. Die Google Jungs stellen sich auch dafür wohl ein Save mit Post vor, wie der Auszug aus der Doku erkennen lässt.
Man findet im Web einige Workarounds. Auch die offizielle Dokumentation von $resource zeigt einen Lösungsansatz für update und PUT.
Geht natürlich. Aber Entwickler muss bei jeder Verwendung von $resource daran denken, der Code wird umfangreicher und damit auch nicht lesbarer.
Die Lösung ist der $resourceProvider, eine weitere Variante eines Angular Services. Damit kann beim Start der Single Page Application $resource konfiguriert werden. So wird eine update Methode angelegt, die eine ID im Parameter erwartet (ID findet sich im URL String :ID) und per PUT an den Web Server gesendet wird. Bei der Gelegenheit wurde hier auch DELETE umkonfiguriert. Das erklärt Zeile 11 aus dem vorigen JavaScript Code.
1: angular.module("App")
2: .config( function ($resourceProvider) {
3: angular.extend($resourceProvider.defaults.actions, {
4: update: { method: 'PUT', params: { ID: '@Id' } },
5: delete: { method: 'DELETE', params: { ID: '@Id' } }
6: });
7: });
Diesen Lösungsansatz findet man allerdings nirgends. Gründe bisher unbekannt.