en
de

REST basierte Webapplikation mit Jersey und AngularJS – Teil 2

24 Oktober 2013
| |
Lesezeit: 7 Minutes

Nachdem sich der erste Teil dieser zweiteiligen Blog-Serie mit der Server-Seite beschäftigt hat, geht es in diesem zweiten Beitrag nun um die Client-Seite, welche mit AngularJS umgesetzt wird.

Eingesetzte Tools, Umgebung

Für die Umsetzung des Beispielprojekts habe ich folgende Umgebung eingesetzt:

Folgende Libraries werden in diesem Tutorial eingesetzt:

Über AngularJS

Die wichtigsten AngularJS Direktiven im Überblick:

  • ngApp
    Ermöglicht die Definition eines Moduls. Auf diesem werden sämtliche Artifakte (Controller, Services, …) von AngularJS registriert. Eine Applikation kann aus mehreren Modulen bestehen.
  • ngController
    Erlaubt das hinterlegen eines Controllers zu einem bestimmten HTML Element. Das entsprechende Element und seine Kinder (Childs) haben dann Zugriff auf sämtliche vom Controller im $scope hinterlegten Variabeln und Funktionen.

  • ngView
    Bestandteil des Templating in AngularJS. Unterhalb des Elements, welches mit dieser Direktive annotiert wird, werden die sogenannten Partials (Subviews) hineingeladen.

  • ngRepeat
    Ermöglicht das iterieren über ein Array von Objekten. Innerhalb einer einzelnen Iteration kann auf die einzelnen Attribute des aktuellen Objekts zugegriffen werden.

  • ngClick
    Funktionen die in einem Controller definiert wurden können mit dieser Direktive direkt an einen Button oder Link gebunden werden.

  • ngClass
    CSS Klassen können mit diesem Attribute abhängig von Bedingungen eingefügt werden. Ermöglicht es beispielsweise, abhängig von der aktuellen URL einzelne Navigationselemente optisch hervorzuheben.

  • ngModel
    Ermöglicht ein bidirektionales Data-Binding zwischen dem Model und der View. Wird vor allem in Formularen verwendet, wenn es darum geht, die einzelnen Formularfelder mit Attributen eines Models zu verbinden.

MVC in AngularJS

Das wichtigste Konzept in AngularJS ist das bidirektionale Data-Binding zwischen Model und View (siehe Grafik). Wenn Model und View einmal an einander gebunden sind, werden Änderungen auf der einen Seite unmittelbar auf die andere Seite gespiegelt. Ergänzend übernimmt der Controller in AngularJS die Aufgabe ein, sämtliche Interaktion(en) mit dem Benutzer zu verarbeiten und falls notwendig entsprechende Anpassungen am Model vorzunehmen.

Grafische Darstellung der Funktionsweise von MVC in AngularJS.

Grafische Darstellung der Funktionsweise von MVC in AngularJS.

Hinweise zum Tutorial

Dieses Tutorial deckt der Einfachheit halber lediglich die Darstellung der Übersichtsseite mit allen Lernkarteien ab. Die Anzeige der einzelnen Karten innerhalb einer Lernkartei kann sehr ähnlich umgesetzt werden.

Design Mocks

Die Beispielapplikation des Tutorials ist eine elektronische Lernkartei. Damit deren Aufbau und Funktionsweise einfach und verständlich ist, wurde diese bewusst einfach gehalten. Folgender Design Mock soll einen Eindruck davon vermitteln, wie die Übersicht über alle Lernkarteien aussehen könnte.

Übersicht über sämtliche Lernkarteien

Übersicht über sämtliche Lernkarteien

Die Umsetzung mittels AngularJS

Index.html

Single Page Application

Der Startpunkt jeder AngularJS Applikation und Webapplikationen ist die Index.html Seite. Hier werden einerseits sämtliche JavaScript und CSS Dateien integriert und gleichzeitig erste AngularJS Direktiven eingebunden. In der Lernkartei-Applikation geht es hier darum, das Grundlayout zu definieren und die Navigation einzubinden. Mittels folgender Zeile

<html ng-app="Flashcards">

wird das Modul Flashcards dem HTML Element zugewiesen. Es handelt sich hierbei um das einzige Modul der ganzen Applikation. Auf diesem werden alle benötigten AngularJS Komponenten registriert.
Ein weitere wichtige AngularJS Direktive wird in folgender Zeile eingesetzt

<div ng-view>
	<!-- AngularJS views are added here at runtime. -->
</div>

Diese definiert einen sogenannten Platzhalter, an dem dann (je nach URL) unterschiedliche Subviews (Partials) hineingeladen werden können. Mehr dazu später im Abschnitt über die Routen.
Die restlichen Zeilen in der Index.html Datei dienen lediglich dazu, ein grobes Layout zu definieren und dieses durch die Anwendung von verschiedenen CSS Klassen aus der Twitter Bootstrap Library optisch ansprechend zu gestalten.

Schlussendlich sollte die Index Seite ungefähr so aussehen (mit Ausnahme der fehlenden Navigation, welche im nächsten Abschnitt erläutert wird).

Demo

Header der Beispielapplikation

Header der Beispielapplikation

Quellcode

<html ng-app="Flashcards">
	<head>
		<title>Flashcards</title>
		<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

		<!-- HIER KÖNNEN CSS UND JAVASCRIPT DATEIEN EINGEBUNDEN WERDEN -->
	</head>
	<body>
		<div class="container">
			<header>
				<div class="page-header">
					<h1>Flashcards <small>Lernkartei mit AngularJS und REST</small></h1>
				</div>
				<div class="navbar" ng-controller='NavigationController'>
					<!-- HIER WIRD DIE NAVIGATION EINGEBUNDEN. DAS SNIPPET UND DIE ERKLÄRUNG DAZU FOLGEND WEITER UNTEN IM TUTORIAL -->
				</div>
			</header>
			<div ng-view>
				<!-- AngularJS views are added here at runtime. -->
			</div>
		</div>
	</body>
</html>

Navigation

Die Navigation wird wie gewohnt mit Aufzählungszeichen definiert. Zusätzlich werden noch einige Bootstrap CSS Klassen hinzugefügt, die sicherstellen, dass die Navigation sich auch auf mobilen Geräten gut bedienen lässt und optisch etwas hergibt. Die einzigen AngularJS Direktive, welche hier verwendet wird, ist ngClass. Wie oben bereits erläutert, bewirkt diese, dass abhängig von einer Bedingung eine CSS Klasse hinzugefügt wird. Im Fall der Navigation wird hier geprüft, ob die aktuelle Route einem bestimmten Tab entspricht. Falls diese Bedingung zutrifft, wird die Klasse active dem Link hinzugefügt. Dies führt dazu, dass der entsprechende Link optisch hervorgehoben wird, damit der Benutzer in der Navigation sofort erkennen kann, auf welcher Seite er sich gerade befindet.

Demo

Navigation der Beispielapplikation

Navigation der Beispielapplikation

Quellcode

<div class="navbar-inner">
	<div class="container">
		<a class="btn btn-navbar" data-toggle="collapse" data-target=".navbar-responsive-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </a>
		<a class="brand" href="#lernkartei">Lernkarteien</a>
		<div class="nav-collapse collapse navbar-responsive-collapse">
			<ul class="nav">
				<li ng-class="{active: $route.current.activeTab =='hilfe'}">
					<a href="#hilfe">Hilfe</a>
				</li>
				<li ng-class="{active: $route.current.activeTab =='links'}">
					<a href="#links">Links</a>
				</li>
				<li ng-class="{active: $route.current.activeTab =='impressum'}">
					<a href="#impressum">Impressum</a>
				</li>
			</ul>
		</div>
	</div>
</div>

Partial

Bei einem Partial handelt es sich um eine sogenannte Subview. Diese wird abhängig von der aktuellen Route in das HTML Element geladen, welches mit ngView annotiert wurde (siehe oben im Abschnitt Index.html). Jedes dieser Partials hat einen eigenen Controller, der die Daten zur Verfügung stellt und allfällige Callbacks bereithält.
Im Falle der Beispielapplikation gibt es eine spezifische Subview für die Übersicht über alle Lernkarteien. Hier stellt der Controller (mehr dazu später) ein Array von Lernkarteien (carddecks) zur Verfügung. Über dieses wird im Partial unter Verwendung der ngRepeat Direktive iteriert. Unterhalb des ngRepeat kann dann auf die Attribute der aktuellen Lernkartei zugegriffen werden.

Demo

Lernkartei Beispielapplikation Partial Partial der Beispielapplikation welches die Übersicht zeigt

Partial der Beispielapplikation welches die Übersicht zeigt

Quellcode

<div id="content">
	<h2>Übersicht Lernkarteien</h2>
	<div class="row-fluid">
		<div ng-repeat="carddeck in carddecks" class="span4">
			<div class="thumbnail">
				<a href="#karten/lernkartei/{{carddeck.id}}" title="Lernkartei #{{carddeck.id}} auswählen">
					<img ng-src="img/{{carddeck.coverImage}}">
				</a>
				<h3>{{carddeck.title}}</h3>
				<p>{{carddeck.numberOfCards}} Karten</p>
			</div>
		</div>
	</div>
</div>

Controllers

Der Controller in AngularJS ist dafür verantwortlich, die Daten für die View bereitzustellen und falls notwendig aufzubereiten. Des Weiteren stellt er der View Funktionen zur Verfügung, die beispielsweise bei einem Klick auf einen Button oder bei einer sonstigen Interaktion des Benutzers aufgerufen werden können.
Sämtliche Daten & Funktionen innerhalb des Controllers, welche für die View sichtbar und benutzbar sein sollen müssen in die $scope-Variable geschrieben werden. Auf alle Daten, die dieser Variable zugeordnet werden, kann die View zugreifen.

Quellcode

Flashcards.controller('CardDeckListController', function($scope, CardDeckService) {
  $scope.carddecks = CardDeckService.findAllCardDecks();
});

Services

Services sind in AngularJS Singletons, die mittels Dependency Injection einem Controller hinzugefügt werden können. Das spezielle an den Services ist, dass diese erst dann instanziert werden, wenn sie effektiv benötigt werden. In unserer Beispielapplikation wird beispielsweise der CardDeckService in den CardDeckListController injiziert in dem er als Parameter der Funktion mitgegeben wird. In der Beispielapplikation werden die Services primär für die Interaktion mit der REST-Schnittstelle verwendet. Ihre Aufgabe ist es, dem Controller entsprechende Finder-Methoden zur Verfügung zu stellen, welche via REST die gewünschten Daten abgreifen. Dies geschieht unter Verwendung des $resource Objekts von AngularJS, welches den Zugriff auf HTTP Ressourcen abstrahiert. Für den Datentransfer zwischen den AngularJS Services und der REST Schnittstelle werden JSON-Objekte verwendet.

Quellcode

Flashcards.factory('CardDeckService', function($resource) {
	var cardDeckService = $resource('/flashcards/api/carddecks/:cardDeckID', {}, {});

	cardDeckService.findAllCardDecks = function() {
		return cardDeckService.query();
	};
	cardDeckService.findCardDeckByID = function(id) {
		return cardDeckService.get({ cardDeckID : id });
	};

	return cardDeckService;
});

Routes

Mittels Routen kann in AngularJS definiert werden, welcher Controller und welches Partial (Subview) für eine bestimmte URL geladen werden soll. Für die Darstellung der Lernkarteien in der Beispielapplikation werden 2 Routen verwendet. Eine für die Anzeige sämtlicher Lernkarteien und eine für die Anzeige einer einzelnen Lernkartei (inkl. dazugehörigen Details). Um diese 2 Routen zu definieren wird eine Javascript Variable angelegt (beispielsweise flashcardsConfig), welcher eine Funktion zugeordnet wird, die über die Parameter den $routeProvider injiziert bekommt und auf diesem anschliessend die Routen registriert.
Nebst der Zuweisung des Controllers (controller) und des Partials (templateUrl) wird hier pro Route noch eine eigene Variable (activeTab) definiert. Diese wird in der Navigation (siehe weiter oben) dazu verwendet, das aktive Tab hervorzuheben.

Quellcode

var flashcardsConfig = function($routeProvider) {
	$routeProvider.when('/lernkartei', {
		controller : 'CardDeckListController',
		templateUrl : 'partials/carddeck/carddeck-list.html',
		activeTab : 'carddecks'
	})
	.when('/lernkartei/:cardDeckID', {
		controller : 'CardDeckDetailController',
		templateUrl : 'partials/carddeck/carddeck-detail.html',
		activeTab : 'carddecks'
	})
}
Flashcards.config(flashcardsConfig);

Die fertige Applikation

Die fertige Beispielapplikation (die unter anderem aus den obenstehenden Code-Snippets besteht) sieht dann folgendermassen aus.

Übersicht über alle Lernkarteien

Übersicht über die Lernkarteien in der Beispielapplikation

Übersicht über die Lernkarteien in der Beispielapplikation

Details einer Lernkartei inkl Navigation durch die Fragen

Übersicht über die Karten innerhalb einer Lernkartei in der Beispielapplikation

Übersicht über die Karten innerhalb einer Lernkartei in der Beispielapplikation

Abschluss

Wir sind am Ende des 2-teiligen Tutorials angekommen. Da in den beiden Tutorials jeweils nur einzelne Aspekte der gesamten Beispielapplikation aufgezeigt werden konnten, habe ich unterhalb ein ZIP Archiv beigefügt, in dem das vollständige, lauffähige IntelliJ Projekt (Server + Client) eingebunden ist und Beispieldaten für die Datenbank integriert sind.

IntelliJ Projekt downloaden

Ich hoffe ich konnte dir eine grobe Einführung in die Entwicklung einer REST basierten Webapplikation unter Verwendung von Jersey und AngularJS näher bringen.

Bei Fragen kannst du dich sehr gerne an mich wenden.

Kommentare (6)

Avatar

insbire

22 November 2013 um 04:13

Mit welche URL kann man die deployed App zugreifen? Warum gib’s bei mir immer HTTP Status 404 Fehler??? LG

——————————————————–

HTTP Status 404 – /carddecks/

type Status report

message /carddecks/

description The requested resource is not available.

Apache Tomcat/7.0.40

Avatar

Christof

4 Dezember 2013 um 17:07

Danke für das tolle Tutorial. Funktioniert einwandfrei!

    Marc Baur

    Marc Baur

    5 Dezember 2013 um 09:54

    Hallo Christof

    Herzlichen Dank für deine Rückmeldung. Das ist schön zu hören.

    Viele Grüsse
    Marc

×

Updates

Schreiben Sie sich jetzt ein für unsere zwei-wöchentlichen Updates per E-Mail.

This field is required
This field is required
This field is required

Mich interessiert

Select at least one category
You were signed up successfully.

Erhalten Sie regelmäßige Updates zu neuen Blogartikeln

Jetzt anmelden

Oder möchten Sie eine Projektanfrage mit uns besprechen? Kontakt aufnehmen »