Single Page Applications mit AngularJS, Teil 2: Konzepte näher betrachtet

Werkzeuge  –  0 Kommentare

AngularJS erfreut sich derzeit großer Beliebtheit. Ein Blick auf die Konzepte hinter dem Framework und ausgewählte Dienste helfen, es besser zu verstehen.

Nachdem der erste Teil der Artikelserie einen Senkrechtstart in AngularJS geboten hat, widmet sich der vorliegende zweite Teil ausgewählten Konzepten des populären Frameworks etwas intensiver. Dazu gehören Direktiven, Scopes, Filter und Services. Darüber hinaus ist der AngularJS-Service $http ein Thema.

Bis jetzt wurden Direktiven ausschließlich in Form von Attributen genutzt. Darüber hinaus lassen sie sich allerdings auch als HTML-Elemente, HTML-Kommentare und als Klassen, die man mit dem Attribut class kennzeichnet, verwenden. Dabei ist jedoch zu beachten, dass nicht jede Direktive zwangsläufig jede Form unterstützt. Das folgende Beispiel aus der Dokumentation von AngularJS veranschaulicht die einzelnen Varianten:

<my-dir></my-dir>
<span my-dir="exp"></span>
<!-- directive: my-dir exp -->
<span class="my-dir: exp;"></span>

Der Ausschnitt zeigt zudem, dass AngularJS das direkte Übergeben eines Werts an eine Direktive bei deren Einsatz als HTML-Element nicht unterstützt. In allen anderen Fällen wird der Wert exp übergeben. Am häufigsten findet man in der Praxis Direktiven, die als Attribute und Elemente zum Einsatz kommen, wobei die Verwendung als Element bei älteren Browsern zu Problemen führt.

Es existieren mehrere Möglichkeiten, eine Direktive als Attribut zu verwenden:

<input ng-model="name">
<input ng:model="name">
<input ng_model="name">
<input data-ng-model="name">
<input x-ng-model="name">

Neben der bis dato verwendeten Variante, die sich auf den häufig verwendeten Präfix ng- stützt, lassen sich unter anderem der XHTML-kompatible Präfix ng: sowie das HTML5-kompatible data- verwenden. Das kann dann wichtig sein, wenn eine erfolgreiche formale Prüfung gegen XHTML oder HTML5 angestrebt wird.

Direktiven können entweder den vorhandenen Scope nutzen oder einen neuen erstellen. Die im letzten Beispiel verwendete Direktive ng-model zieht etwa den Scope heran, den der Controller erstellt. In ihm sucht ng-model nach der zu bindenden Eigenschaft. Die Direktive ng-repeat erzeugt hingegen einen eigenen Scope pro Array-Eintrag, für den sie das jeweilige Element wiederholt. Im Beispiel erhält dieser Scope die Eigenschaften f und $index, die den aktuell bearbeiteten Flug sowie dessen Position innerhalb des Arrays repräsentieren.

Darüber hinaus ist der Scope über die Prototyp-Kette, die in JavaScript zur Realisierung von Vererbung zum Einsatz kommt, mit dem übergeordneten Scope, den der Controller bereitstellt, verbunden. An der Spitze der Kette steht ein sogenannter $rootScope, der für die gesamte Anwendung gilt (siehe Abb. 1).

Verknüpfung von Scopes über Prototypen-Kette (Abb. 1)

Dadurch, dass die Scopes über die Prototypen-Kette verbunden sind, kann der Entwickler innerhalb des Scopes ng-repeat, sprich innerhalb des Elements, welches das Attribut ng-repeat aufweist, lesend auf die Variable vm im übergeordneten Scope zugreifen. Würde jedoch ng-repeat oder eine darin geschachtelte Direktive, die denselben Scope verwendet, schreibend auf vm zugreifen, würde sie eine Eigenschaft vm im Scope von ng-repeat einrichten. Diese würde die gleichnamigen Eigenschaft im Scope von ng-controller seiner Wirksamkeit berauben.

Anders gestaltet es sich, wenn der Entwickler im Scope von ng-repeat auf vm.flugNummerFilter zugreift. Unabhängig davon, ob der Zugriff lesend oder schreibend erfolgt, sucht JavaScript zunächst nach der Eigenschaft vm und wird im Scope von ng-controller fündig. Anschließend greift es bei dem gefundenen Objekt lesend oder schreibend auf die Eigenschaft flugNummerFilter zu. Das ist auch der Grund, warum es sich beim Einsatz von AngularJS eingebürgert hat, die zu bindenden Eigenschaften nicht direkt im Scope abzulegen, sondern dort ein Objekt zu deponieren, das sie enthält:

<script src="~/Scripts/angular.js"></script>

<script>

var app = angular.module("ScopeHorrorApp", []);

app.controller("ScopeHorrorCtrl", function ($scope) {

$scope.name = "Max Muster";

$scope.person = {};
$scope.person.name = "Susi Sorglos";

$scope.projekte = ["Projekt-A", "Projekt-B", "Projekt-C"];

});
</script>

<div ng-app="ScopeHorrorApp">

<div ng-controller="ScopeHorrorCtrl">

<input ng-model="name" /> <br />
<input ng-model="person.name" />

<div ng-repeat="p in projekte"
style="border: 2px solid black; margin: 5px;">
Projekt: {{ p }} <br />
Projektleiter 1: <input ng-model="name" /> <br />
Projektleiter 2: <input ng-model="person.name" />
</div>

</div>

</div>

Der Scope des Controllers erhält im Quelltextausschnitt eine Eigenschaft name und eine Eigenschaft person, die auf ein Objekt mit einer weiteren Eigenschaft name verweist. Darüber hinaus versieht der Controller den Scope mit einem Array samt Projektbezeichnungen. Die Direktive ng-repeat wiederholt ein div-Element pro Projekt. Neben dem Projektnamen enthält es zwei Textfelder – das eine ist an name gebunden, das andere an person.name.