Part 3 – AngularJS 1/2

12. August 2013

Im letzten Artikel haben wir schon die Mächtigkeit von knockoutJS kennengelernt und wie angenehm es ist, bekannte Patterns aus der Desktopentwicklung auch in der Webentwicklung anwenden zu können.

Heute nehmen wir uns AngularJS vor und erkunden, was uns mit diesem Framework für Möglichkeiten geboten werden. Ich hatte im letzten Artikel schon angedeutet, dass auch AngularJS über die Möglichkeit von Data Binding und Two-Way-Binding verfügt. Neben diesen schon sehr starken Features, gibt es aber noch weitere Punkte, die AngularJS eine Klasse besser machen.

AngularJS

AngularJS kommt von Google und ist mit 80kb schon doppelt so groß wie das im letzten Artikel vorgestellte knockoutJS. Dadurch wird deutlich, dass AngularJS deutlich mehr Funktionen bietet. Folgender Funktionsumfang steckt in AngularJS:

Eine ganze Latte an coolen Features, von denen ich in diesem Artikel aber nicht alle darstellen werde.

Im Vergleich zu knockoutJS, welches im Wesentlichen aus Observables, ViewModels und Views besteht, gibt es bei AngularJS noch ein bisschen mehr Infrastruktur, um die man sich “kümmern” muss. Ein Teil der Infrastruktur sind z.B. Services. Über diese bildet man zum Beispiel Funktionalitäten wie Datenzugriff ab.

In AngularJS erstellen wir unser UI, ähnlich zu knockoutJS, mithilfe von Views und Templates. Die Templates werden in AngularJS nicht wie in knockoutJS durch ViewModels gefüllt, sondern durch Controller, was aber von der Funktionsweise her genau dasselbe ist. In beiden Fällen wird der Controller bzw. das ViewModel an die View gebunden und beim Ändern der Eigenschaften des Controllers bzw. des ViewModels wird die Darstellung im View angepasst. Genauso passiert es auch in die andere Richtung. Daten, die im View (z.B. durch Eingaben) geändert werden, werden automatisch in die Eigenschaften des Controllers bzw. des ViewModels geschrieben.

AngularJS bietet zudem ein Modulsystem, über das sich Abhängigkeiten zu anderen Modulen durch Dependency Injection einbinden lassen. Das bringt uns eine lose Kopplung und ist ein großer Vorteil für die Testbarkeit, auf die ich gegen Ende der Serie noch kurz eingehen werde.

Vorbereitung

Als Erstes brauchen wir eine Projektstruktur. Dafür gibt es auf GitHub ein Seed Projekt, welches eine initiale Struktur bereitstellt.

Für unsere Testanwendung werden wir einen Service erstellen, der sich um den Datenzugriff (Todo Einträge) kümmert. Wir werden einen Controller, der diesen Service injiziert bekommt und eine View zum Anzeigen und “kontrollieren” der Todo-Einträge erstellen.

Neben den Standardfunktionen zur Verwaltung einer Todo-Liste, wie Hinzufügen und Abhaken von Einträgen, werden wir noch mit wenigen Handgriffen eine Filterfunktion für die Todo-Einträge ergänzen. Außerdem werden wir ein zweites View (About) einbauen, welches über die Routing-Funktionalität von AngularJS angesteuert werden kann.

Beginnen wir als Erstes mit dem Konfigurieren der Anwendung und der Routen (app/js/app.js):

   1: angular.module('todoApp', [ 'todoApp.services', 'todoApp.controllers'])

   2:   .config(['$routeProvider', function(routes) {

   3:     routes.when('/todos',{templateUrl: 'partials/todos.html', controller: 'TodoController'});

   4:     routes.when('/about', {templateUrl: 'partials/about.html', controller: 'AboutController'});

   5:     routes.otherwise({redirectTo: '/todos'});

   6:   }]);

Neben dem Routing sieht man hier sehr gut, wie Module erstellt und konfiguriert werden. Zunächst wird ein Name für das Modul benötigt, in unserem Fall todoApp. Als Zweites werden Abhängigkeiten des Moduls über ein Array hinzugefügt. Die todoApp.services und todoApp.controllers gibt es an dieser Stelle noch nicht, diese werden aber in den nächsten Schritten erzeugt.

Die Konfiguration des Moduls geschieht über die config(configFn) Funktion. Diese Funktion wird ausgeführt, sobald das Modul geladen wird. Es ist also ein guter Einstiegspunkt, um die Routen für die Views zu erstellen. Dafür benötigen wir den von AngularJS mitgebrachten $routeProvider.

Damit der $routeProvider verfügbar ist, übergibt man der config(configFn) ein Array und nicht wie erwartet eine Funktion. Das ist ein typisches Beispiel, um Abhängigkeiten innerhalb einer Funktion/Klasse/Modul verfügbar zu machen. In dem Array werden zunächst sämtliche Abhängigkeiten (meist als Strings) angegeben und der letzte Eintrag des Arrays ist die auszuführende Funktion. Dieser Funktion werden die vorangestellten Parameter übergeben, und zwar in der angegeben Reihenfolge.

Somit enthält der routes-Parameter den $routeProvider und wir können diesem nun unsere Routen und die dazugehörigen Aktionen mitteilen.

Der $routeProvider besitzt dafür zwei Funktionen:

  • when(path, routeObject)
  • otherwise(routeObject)

Mit when(..) definiert man die Aktion, die aufgerufen werden soll, wenn eine bestimmte URL aufgerufen wird. Wohingegen otherwise(..) in allen anderen Fällen aufgerufen wird.

In unserem Beispiel haben wir drei Routen definiert:

  • /todos –> nutzt als View die Datei /partials/todos.html und als Controller TodoController.
  • /about –> nutzt als View die Datei /partials/about.html und als Controller AboutController.
  • otherwise –> Standardmäßig soll auf die /todos Seite umgeleitet werden, wenn eine unbekannte URL aufgerufen wurde.

Als nächsten Schritt erstellen wir das HTML-Grundgerüst für die Anwendung. (app/index.html):

   1: <!doctype html>

   2: <html lang="en" ng-app="todoApp">

   3: <head>

   4:   <meta charset="utf-8">

   5:   <title>AngularJS - Todo</title>

   6:   <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"/>

   7:   <link rel="stylesheet" href="css/app.css"/>

   8: </head>

   9: <body>

  10:   <div class="navbar">

  11:     <div class="navbar-inner">

  12:       <a class="brand" href="#">Todo Liste</a>

  13:       <ul class="nav">

  14:         <li><a href="#/todos">Todos</a></li>

  15:         <li><a href="#/about">About</a></li>

  16:       </ul>

  17:     </div>

  18:   </div>

  19:   <div ng-view></div>

  20:   <script src="lib/angular/angular.js"></script>
   1:  

   2:   <script src="js/app.js">

</script>

   1:  

   2:   <script src="js/services.js">

</script>

   1:  

   2:   <script src="js/controllers.js">

</script>

  21: </body>

  22: </html>

Zu beachten ist hier, dass wir im <html>-Tag das ng-app Attribut finden, welches kein Standard HTML-Attribut ist, sondern eine in AngularJS integrierte Directive. Von diesen Directiven kann man natürlich auch eigenen erstellen. Mit dem ng-app Attribut wird unser todoApp-Modul der Seite zugeordnet. Innerhalb dieses Tags sind damit alle Elemente im Zugriff von AngularJS und somit unserer todoApp.

Im Vergleich zu knockoutJS verlässt AngularJS hier den Standard HTML5 data-xy Weg und verwendet eigene Attribute.

Im <body>-Element sehen wir eine kleine Liste mit <a>-Elementen. Darüber wird in der Anwendung die Navigation durchgeführt. Die href-Attribute zeigen dabei auf die von uns festgelegten Routen. Somit wird nachher – in der Anwendung – beim Aufrufen dieser Links die entsprechende Navigationslogik ausgeführt.

In unseren Routen haben wir angegeben, welche Views zur entsprechenden Route gehören. Beim Eintreten einer Navigation werden diese Views an einen bestimmten Platz geladen. Dieser Platz befindet sich unter der Navigationsleiste und besitzt ein weiteres Attribut aus dem AngularJS Universum: ng-view.

Der Routing Service nutzt dieses ng-view Element, um sämtliche Views dorthinein zu rendern.

An dieser Stelle sind wir am Ende dieses Beitrags. Im nächsten Part nutzen wir die bereits aufgebaute Infrastruktur und bringen unseren Service, die Controller, die Views und den Filter ins Spiel.

Fazit

Wir haben jetzt schon jede Menge gelernt und trotzdem relativ wenig Code geschrieben, um unsere kleine Todo-Liste fertig zu stellen. Das zeigt, was ich Eingangs bereits erwähnt habe: In AngularJS gibt es ein wenig mehr Infrastruktur zu handhaben, als es z.B. in knockoutJS der Fall ist. Das ist allerdings nichts Schlechtes, da es dadurch nachher leichter wird, modularer und testbarer zu entwickeln.

Es kann auch mit weniger Aufwand so eine kleine Todo-Listen-Anwendung geschrieben werden. Das ist hier allerdings nicht das Ziel, weil wir verstehen wollen, was mit AngularJS alles schönes gemacht werden kann 😉

Stay tuned 🙂