Multi-Device-Hybrid Apps mit Visual Studio 2013 und Cordova

Cordova ist mittlerweile bestimmt jedem ein Begriff. Es ist in der Regel das Framework, wenn es um die plattformübergreifende Entwicklung hybrider Anwendung mit HTML, JavaScript und CSS für mobile Geräte geht.

Dadurch dass es mittlerweile sogar eine Visual Studio Erweiterung gibt, zeigt die Bedeutung von Cordova. Ich will in diesem Beitrag kurz auf diese Erweiterung eingehen und den ein oder anderen nützlichen Hinweis für die Verwendung geben.

Plugins

Folgende Plugins sind für Cordova schon direkt vorhanden und können einzeln dazu geschaltet werden. Damit können die nativen Features mit JavaScript aufgerufen werden:

Installation

Damit man unter Visual Studio 2013 mit der Cordova Entwicklung loslegen kann, muss man sich zuerst die Erweiterung herunterladen:

http://msdn.microsoft.com/en-us/vstudio/dn722381.aspx

Nach der Installation befindet sich unter den Projekt-Templates ein neuer Eintrag zum Erstellen von einer Multi-Device-Hybrid App. Wählt man dieses aus kann man eine “Blank App” erzeugen lassen.

Ausführen

Hierbei erzeugt Visual Studio uns eine vordefinierte Projektstruktur und gibt uns auch neue Ausführoptionen zum Starten der Anwendung über F5.

image

Zum Debuggen der Anwendung kann entweder ein Device herangezogen werden, ein Emulator oder Ripple.

Ripple ist eine Browsererweiterung für Chrome, mit der das Ausführen von Cordova Anwendungen ermöglicht wird. Ich persönlich bin kein Freund davon, weil eigentlich alle Cordova Plug-Ins damit nicht funktionieren.

Für einen Test wählt man nun z.B. Windwos-AnyCPU und drückt F5. Cordova Anwendungen werden normalerweise über ein Command-Line-Tool erstellt. Diese Schritte werden jetzt im Hintergrund von Visual Studio übernommen. Visual Studio erzeugt nun also im Hintergrund für die ausgewählte Plattform die entsprechende Cordova Anwendung. Diese wird im Solution Verzeichnis unter dem Verzeichnis bld/Debug/platforms angelegt.

Wer sich mit Cordova auskennt, wird die Verzeichnisstruktur kennen, die sich unter bld/Debug befindet. Diese ist nämlich die übliche Struktur, die auch erzeugt wird, wenn über das Command-Line-Tool eine Cordova Anwendung erzeugt wird.

Nachdem die Anwendung erstellt und alle Plug-Ins heruntergeladen wurden, wird die Anwendung gestartet. Im Falle von Windows-AnyCPU ist es eine Windows 8 App.

Möchte man nun sehen wie das Ganze auf einem Android-Gerät aussieht wählt man einfach in den Ausführungsoptionen den entsprechenden Eintrag aus und drückt wieder auf F5.

Auch hier wird unter bld/Debug/platforms das entsprechende Projekt für Android erstellt.

Nach diesem Prozess wird die Anwendung gestartet (Je nachdem was man ausgewählt hat auf dem Device oder im Emulator).

So leicht wie bei Windows und Android kann man die iOS Version der App leider nicht erstellen. Hierfür wird ein Mac mit XCode und Entwicklerlizenz benötigt.

Auf dem Mac muss zudem noch ein Stück Software in Form einer NodeJS-Anwendung installiert werden, mit dieser das Visual Studio dann kommuniziert um die Anwendung von dem Entwicklungsrechner auf den Mac zu bekommen. Auf dem Mac wird dann das Project für XCode generiert (Die Installation der NodeJS-Anwendung ist in der Anleitung beschrieben, die bei der Installation dabei ist).
Laut Microsoft Dokumentation, soll die Anwendung automatisch auf das am Mac Angeschlossene Gerät deployed werden. In meinen Versuchen hat das leider nicht funktioniert. Ich musste auf dem Mac das XCode-Projekt öffnen und selber auf das Gerät deployen. Ich persönlich habe damit aber kein Problem.

Debuggen

Neben dem Vorteil, dass Visual Studio für einen die Command-Line bedient und sich um das Erzeugen der Anwendung sowie das Besorgen der Plugins bemüht, ist es sehr hilfreich das man die Anwendung einfach mit einem Breakpoint im Code Debuggen kann. Dies funktioniert sogar, wenn die Anwendung auf einem Android Gerät ausgeführt wird.

Windows 8.1

Aktuell wird für die Erzeugung der Windows App noch die Version 8 als Target Plattform gewählt. Wer das gerne ändern möchte kann dies in dem Template, welches für die Erzeugung der Windows Version herangezogen wird ändern. Das ist zum Beispiel unerlässlich wenn man eine eigene Version des InAppBrowsers entwickeln möchte, die das mx-webview-Control verwendet anstelle eines IFrames. Das mx-webview-Control ist nämlich erst ab Windows 8.1 verfügbar.

Das Template befindet sich unter:

C:UsersUsername.cordovalibwindows8cordova3.5.0windows8 emplate

Dort öffnet man im Editor seiner Wahl die Datei CordovaApp.jsproj und ändert die TargetPlatformVersion auf 8.1.

Plugins und Custom Plugins

Ein Grund weshalb man überhaupt auf Cordova angewiesen ist, liegt darin dass man auf die nativen Eigenschaften eines Gerätes zugreifen will wie z.B. die Kamera, die Kontakte usw. diese werden bei Cordova über Plug-Ins zur Verfügung gestellt.

Mit der Visual Studio Extension für Cordova kann man vorgefertigte Plug-Ins über ein grafisches Interface dazu schalten oder wieder abschalten.

Möchte man allerdings eigene Plug-Ins bereitstellen, kann man das aktuell noch nicht über ein grafisches Interface erledigen.

Es muss händisch das Verzeichnis “plugins” im Root der Solution erzeugt werden. Dort hinein packt man dann in Unterordner seine Plug-Ins. Beim Builden (F5) werden diese Plug-Ins dann automatisch in der Anwendung registriert.

Fazit

Mit der Cordova Erweiterung Multi-Device-Hybrid Apps für Visual Studio 2013 ist das arbeiten mit Cordova erheblich erleichtert worden. Die Erweiterung nimmt einem viel Arbeit mit der Command-Line ab und erstellt Plattformen, Plug-Ins usw. per Knopfdruck. Auch wenn es aktuell noch in der Preview Version vorliegt, macht die Erweiterung einen guten Eindruck.

Do more with LESS

Eigentlich ist LESS ja ein alter Hut und im Web finden sich mehr Beiträge darüber welchen Präprozessor man verwenden soll, als ob überhaupt einer verwendet werden sollte. Das ist eine gute Sache, weil unabhängig für welchen Präprozessor man sich entscheidet, die Arbeit mit CSS erheblich erleichtert wird.

Man erkennt schon nach wenigen Minuten die Vorteile, die einem z.B. durch den Einsatz von LESS geschenkt werden. Man kann viel strukturierter und auf Komponentenbasis CSS-Dateien erstellen (lassen). Hierfür muss man sich nur an eine einfache und schnell zu erlernende Syntax gewöhnen. Aber auch ohne die Syntax zu beherrschen kann man schon valides LESS schreiben, denn CSS ist ebenfalls valider LESS-Code. CSS wird also nur durch viele hilfreiche Funktionen erweitert.

Ein paar Beispiele zum Anheizen

Variablen: Mit Variablen hat man die Möglichkeit Werte, die an verschiedenen Stellen benötigt werden, an einer Stelle zu definieren. Dadurch kann mit einer Änderung der Wert für alle verwendeten Bereiche verändert werden. Was aber viel cooler ist, ist das man mit Variablen rechnen kann. Man kann sich also Höhen und Breiten oder aber auch Farbwerte ausrechnen.

LESS:

   1: @padding10: 10px;

   2:  

   3: .page{

   4:     padding: @padding10;

   5: }

   6: .menu{

   7:     padding-left: @padding10 + 5px;

   8: }

CSS:

   1: .page{

   2:     padding: 10px;

   3: }

   4: .menu{

   5:     padding-left: 15px;

   6: }

Mixins: Mixins helfen dabei redundante CSS-Anweisungen zu eliminieren indem man den redundanten Part einfach als Mixin schreibt und an den gewünschten Stellen verwendet. Ein gutes Beispiel dafür sind die Browser Prefixe von manchen CSS-Properties. Wie das aussieht zeigt folgendes Codebeispiel:

LESS:

   1: .backface-hidden {

   2:   -moz-backface-visibility: hidden;

   3:   -webkit-backface-visibility: hidden;

   4:   backface-visibility: hidden;

   5: }

   6:  

   7: .menu{

   8:     .backface-hidden;

   9:     position: fixed;

  10:     top: 0px;

  11: }

  12:  

  13: .sidemenu{

  14:     .backface-hidden;

  15:     position: fixed;

  16:     right: 0px;

  17:     bottom: 0px;

  18: }

CSS:

   1: .backface-hidden {

   2:   -moz-backface-visibility: hidden;

   3:   -webkit-backface-visibility: hidden;

   4:   backface-visibility: hidden;

   5: }

   6:  

   7: .menu{

   8:     -moz-backface-visibility: hidden;

   9:     -webkit-backface-visibility: hidden;

  10:     backface-visibility: hidden;    position: fixed;

  11:     top: 0px;

  12: }

  13:  

  14: .sidemenu{

  15:     -moz-backface-visibility: hidden;

  16:     -webkit-backface-visibility: hidden;

  17:     backface-visibility: hidden;    position: fixed;

  18:     right: 0px;

  19:     bottom: 0px;

  20: }

Vererbung”: Mit der “Vererbung” kann man viel natürlicher eine CSS-Klassen Hierarchie abbilden. Dies geschieht indem man die zugehörigen CSS-Klassen einfach innerhalb der gewünschten CSS-Klasse definiert. Was ich damit meine zeigt das nachfolgende Beispiel. Dadurch ist die Zugehörigkeit klar ersichtlich und es ist wesentlich übersichtlicher durch die Einrückungen.

LESS:

   1: .menu {

   2:     list-style: none;

   3:     position: fixed;

   4:  

   5:     & > li {

   6:         display: inline;

   7:         color: #ffffff;

   8:     }

   9:  

  10:     .link{

  11:         color: orange !important;

  12:         font-weight: bolder;

  13:     }

  14: }

CSS:

   1: .menu {

   2:     list-style: none;

   3:     position: fixed;

   4: }

   5: .menu .link {

   6:     color: orange !important;

   7:     font-weight: bolder;

   8: }

   9: .menu > li {

  10:   display: inline;

  11:   color: #ffffff;

  12: }

Module: Des Weiteren kann man andere LESS-Dateien über die @import “path/to/lessfile.less”; importieren und so kleine übersichtliche Module von bestimmten Bereichen erstellen und diese in einer globalen Datei importieren. Damit lässt sich eine CSS-Datei erzeugen, die alle anderen Styles beinhaltet.

Das LESS eine gute Sache ist, erkennt man auch daran, dass man es in den Windows Web Essentials findet. Durch die Installation der Web Essentials, wird automatisch der Compiler für LESS (aber auch für SASS) mitinstalliert. Dadurch muss man sich nicht erst irgendwelche Kommandozeilen Tools oder sogar Ruby installieren, sondern kann in seiner gewohnten Umgebung arbeiten.

Durch die Integration in Visual Studio gibt es nun also keine Ausrede mehr, LESS oder SASS nicht zu verwenden! Zwinkerndes Smiley.

Fazit

Ich hoffe ich konnte diejenigen, die bisher nichts von LESS oder SASS gehalten haben, oder es noch nicht kannten, davon überzeugen auf jeden Fall mal einen tieferen Blick darauf zu werfen. Am Ende erleichtern diese Tools einem nämlich die Arbeit. Das ich in diesem Artikel mehr auf LESS eingegangen bin, hat nix damit zu, dass LESS besser als SASS ist. SASS kann das gleiche was LESS kann und sogar noch mehr und erzeugt in vielen Fällen intelligenteres CSS. Ich wollte nur aufzeigen, dass man selbst schon durch die Verwendung von LESS sein CSS wesentlich besser strukturieren kann.

Wer sich einarbeiten will findet nachfolgend diverse Links zu den Themen LESS und SASS sowie ein paar Vergleiche der beiden.

LESS – Homepage

SASS – Homepage

Vergleich zwischen LESS und SASS

IMHO – Gefährliches Halbwissen

Google hat seine JavaScript Bechmarking Suite Octane auf Version 2.0 gehoben. Dabei hat Google zwei neue Tests eingebaut. Zum einen die zlib Bibliothek, die über asm.js umgesetzt wurde. Und zum Zweiten wurde TypeScript ins Boot geholt.

Laut Google wurde es dazu geholt, “To simulate complex data structure- and memory-intensive applications, we added a test derived from Microsoft’s TypeScript compiler. The result measures how fast TypeScript compiles itself.”

Mir ist noch nicht ganz klar was Google a) mit dieser Aussage und b) mit dem hinzufügen von TypeScript bezwecken wollte. Mir ist bewusst, dass Google und Microsoft nicht die größten Buddys sind, daher vermute ich eine Art „Bloßstellung“ seitens Google Richtung Microsoft.

Der Blogeintrag von Google zu Octane 2.0 wurde auch von hiesiger Presse entdeckt und prompt in eine kleine News gepackt. Scheinbar wurden die Nachforschungen bezüglich TypeScript nur halbherzig gemacht oder dass Ergebniss dieser Nachforschungen nicht richtig verstanden. In einem Artikel heißt es “TypeScript ist eine von Microsoft entwickelte Programmiersprache, die live im Browser nach JavaScript kompiliert.”

Es kann gut möglich sein, dass es beim umwandeln von TypeScript zu JavaScript nicht ganz so schnell von statten geht. Leider spielt dieser Fakt KEINE Rolle für die Laufzeitgeschwindigkeit des mit TypeScript erstellten Programms. Was stimmt ist, dass TypeScript Code zu JavaScript Code kompiliert. Was NICHT stimmt ist, dass TypeScript dies während der Laufzeit tut!

Daher versteh ich nicht, wieso sich Google entschieden hat, TypeScript in eine Testsuite einzubeziehen, die eigentlich dafür gedacht ist Umgebungen/Frameworks zu testen, die dafür gedacht sind während der Ausführung schnell zu sein (“.. Octane‘s goal is to measure the performance of JavaScript code found in large, real-world web applications, running on modern mobile and desktop browsers…” – link). Zum anderen finde ich es schade, dass scheinbar bei der Recherche zu TypeScript, der Verwendungszeck von TypeScript offensichtlich nicht verstanden wurde.

TypeScript wurde von Microsoft entwickelt, um zur Entwicklungszeit eine typsichere Variante von JavaScript zu haben. Zur Entwicklungszeit wird dann, bei jedem Speichern der TypeScript-Quelldatei, sofort in das äquivalente JavaScript umgewandelt. Zumindest ist das in Visual Studio mit den richtigen Plugins der Fall. Ansonsten muss dieser Schritt manuell getan werden. Aber auch hier geschieht dieser Schritt VOR der eigentlichen Ausführung des Programms im Browser. Durch die Typsicherheit und weiteren Features der Sprache, hat man dadurch die Möglichkeit in großen Projekten a) den Überblick zu behalten und b) seine Software auch nach ein paar Monaten noch halbwegs wartbar zu haben.

Aber gut, TypeScript ist nun Teil dieser Testsuite. Offensichtlich um zu zeigen, dass TypeScript einen langsamen Compiler hat. Das ist zunächst mal irgendwie unschön und verfehlt auch völlig den Einsatzgebiet von TypeScript, aber gut … Kann man wohl jetzt nicht mehr ändern.

Wie in letzter Zeit üblich, wird auf alles was aus Richtung Microsoft kommt ziemlich stark rumgehackt und wenn dann auch noch ein Konkurrent kein gutes Wort über ein Produkt verliert, muss das ja stimmen und stumpf wieder gegeben werden.

Das alles ist schade, weil TypeScript von der Sache her eine super Idee ist und für die Entwicklung von großen Projekten mittels JavaScript eine echte Erleichterung darstellen kann.

Fazit

Auch wenn TypeScript in mancher Hinsicht nicht das Gelbe vom Ei ist und der erzeugte JavaScript Code nicht in jeder Beziehung der Beste ist, ist es dennoch eine Erleichterung in großen Projekten. Ich hoffe, ich konnte sie mit diesem kleinen Beitrag dazu bewegen, Dinge zu Hinterfragen und nicht gleich alles zu glauben auch wenn es scheinbar aus einer validen Quelle kommt.

IMHO: In My Humble Opinion

Part 5 – Testing

Im vorherigen Artikel haben wir unsere Anwendung mit AngularJS fertiggestellt. Im aktuellen Artikel geht es darum, wie man seine Anwendung durch Tests auch für eine zukünftige Anpassung fit hält oder vor unbedarften Änderungen schützt. Dafür möchte ich zwei Frameworks nutzen, die von AngularJS empfohlen werden. Karma und Jasmine. Während Karma nur die ausführende Umgebung ist, ist Jasmine ein BDD Testing Framework.

 

Karma

Karma ist ein NodeJS Module und läuft in einer Konsole. Wird Karma gestartet, werden alle Tests ausgeführt. Zunächst einmal nichts Außergewöhnliches. Der Vorteil besteht nun darin, dass beim Ändern der Quellcodedateien sofort alle Tests wieder ausgeführt werden. Man erhält also direkten Feedback ob Codeänderungen dazu führen, dass bestehender Code immer noch oder nicht mehr funktioniert. Natürlich muss die Konsole dafür offen bleiben 😉

Karma führt alle Tests in einem Browser durch. Dadurch kann sichergestellt werden, dass der Code auch wirklich im Browser funktioniert.

Karma kann auch in diverse Continuous Integration Systeme wie Jenkins, Travis und Teamcity eingebunden werden.

Als Testing Framework kann in Karma neben Jasmine (wird von AngularJS präferiert) auch noch Mocha und QUnit benutzt werden.

Praxis

Damit Karma funktioniert, muss es konfiguriert werden. Wenn man sich das angular-seed Projekt heruntergeladen hat, befindet sich dort schon eine karma.conf im config Ordner und eine test.bat im test Ordner. Führt man die test.bat aus, öffnet sich die Konsole und Karma startet den Browser. Der Browser wird in der karma.conf angegeben. Im angular-seed Projekt ist dort standardmäßig Chrome eingetragen. Der Browser darf minimiert, aber nicht geschlossen werden. Das Gleiche gilt für die Konsole. Wobei diese auch nicht minimiert werden sollte, um den direkten Feedback auch zu sehen.

Die Konfiguration sieht folgendermaßen aus:

   1: basePath = '../';

   2:  

   3: files = [

   4:   JASMINE,

   5:   JASMINE_ADAPTER,

   6:   'app/lib/angular/angular.js',

   7:   'app/lib/angular/angular-*.js',

   8:   'test/lib/angular/angular-mocks.js',

   9:   'app/js/**/*.js',

  10:   'test/unit/**/*.js'

  11: ];

  12:  

  13: autoWatch = true;

  14:  

  15: browsers = ['Chrome'];

  16:  

  17: junitReporter = {

  18:   outputFile: 'test_out/unit.xml',

  19:   suite: 'unit'

  20: };

Aber nun genug der Theorie. Testen wir unseren TodoController (test/unit/controllerSpec.js):

   1: describe('Test Controllers', function(){

   2:     beforeEach(function(){

   3:         this.addMatchers({

   4:             toEqualData: function(expected) {

   5:                 return angular.equals(this.actual, expected);

   6:             }

   7:         });

   8:         angular.mock.module('todoApp.controllers');

   9:     });

  10:     describe('TodoController', function(){

  11:         var scope, ctrl, serviceMock;

  12:         beforeEach(inject(function($rootScope, $controller) {

  13:             var data = [{description: 'dies'}, {description: 'das'}];

  14:  

  15:             serviceMock = {

  16:                 get: function(){

  17:                     return data;

  18:                 },

  19:                 add: function(description){

  20:                     data.push({description: description});

  21:                 }

  22:             };

  23:  

  24:             scope = $rootScope.$new();

  25:             $controller('TodoController', {

  26:                         $scope: scope,

  27:                         todoService: serviceMock

  28:                     });

  29:         }));

  30:  

  31:  

  32:         it('should exist two todo items in the todos', function() {

  33:             expect(scope.todos).toEqualData(

  34:               [{description: 'dies'}, {description: 'das'}]);

  35:         });

  36:  

  37:         

  38:         it('should add one new todo entry to the todos Array and clear newTodo Text', function() {

  39:             scope.newTodo = "Woop";

  40:             scope.addNew();

  41:             expect(scope.todos).toEqualData(

  42:                 [{description: 'dies'}, 

  43:                 {description: 'das'}, 

  44:                 { description: 'Woop'}]);

  45:             expect(scope.newTodo).toBe("");

  46:         });   

  47:     });

  48:     describe("AboutController", function(){

  49:         var scope;

  50:         beforeEach(inject(function($rootScope, $controller){

  51:             scope = $rootScope.$new();

  52:             $controller('AboutController', {$scope: scope});

  53:         }));

  54:  

  55:         it('should be "Kleine Anwendung zum verwalten von Todos" ',function(){

  56:             expect(scope.aboutMessage).toBe("Kleine Anwendung zum verwalten von Todos");

  57:         });

  58:     });

  59: });

Hier sehen wir, dass wir einen Test für alle Controller “beschreiben”. Das drücken wir mit describe(‘Test Controllers’,function(){…}); aus.

Für unsere Tests benötigen wir für jeden Controller eine Funktion, die zwei Arrays miteinander vergleicht. Zusätzlich muss das Modul todoApp.controllers als Mock geladen sein. Das wird durch die erste beforeEach(…) Funktion sichergestellt.

Als Nächstes kommen dann schon die Tests für die Controller. Hierfür “beschreibt” man auch wieder was getestet werden soll. Im ersten Fall ist das der TodoController, wo im Inneren die einzelnen Testfälle beschrieben werden.

Bevor allerdings die Testfälle beschrieben werden, müssen wir noch dafür sorgen, dass für jeden Testfall auch eine frische TodoController-Instanz erzeugt wird. Zum Erzeugen der Instanz wird eigentlich unser todoService benötigt. Da wir aber nicht noch eine zweite Abhängigkeit haben wollen, die den Test beeinflusst, erzeugen wir stattdessen einen serviceMock.

An dieser Stelle ist der Vorteil des modularen Aufbaus und der Dependency Injection klar zu erkennen. Wir können dadurch einfach von außen einen eigenen Service implementieren und für die Tests dem Controller injizieren.

Kommen wir nun zu unsere Testfällen.

Für den TodoController wurden zwei Tests erstellt. Der Erste überprüft, dass nach dem erzeugen des TodoController auch etwas in den todos eingestellt ist.

Der Zweite Test überprüft das Hinzufügen eines neuen Eintrags und das daraus resultierende Leeren des eingegeben Textes (Todo Beschreibung).

Speichert man alles ab, wird der Test direkt ausgeführt und sollte folgende Meldung ausgeben:

test1

Soo, nun nehmen wir mal an, wir sind ein anderer Entwickler, der keine Ahnung von unserer Anwendung hat. Dafür öffnen wir mal unsere app/js/controller.js Datei (die wir im letzten Artikel erstellt haben), gehen zu der TodoController.addNew Funktion und entfernen scope.newTodo = “”;.

Augenblicklich wird in der Konsole folgendes zu sehen sein:

test2

Es wird einem also sofort auf die Finger gehauen, wenn Codeänderungen gemacht werden, die zunächst mal so nicht gedacht sind.

Neben Unit Tests, kann man auch noch End-To-End Tests verwenden um z.B. sicherzustellen, dass die Navigation von SeiteA zu SeiteB auch wirklich funktioniert. Oder, dass beim Drücken eines Buttons auch wirklich das div mit der id=”searchResult” sichtbar geschaltet wird. Allerdings muss man den E2E-Test immer manuell ausführen.

Fazit

Wir sind nun am Ende der Serie angekommen. Zeit also für ein kleines Fazit.

Wir haben in den letzten Artikeln das JavaScript-Framework knockoutJS und AngularJS betrachtet und festgestellt, dass die Frameworks vieles für den Entwickler übernehmen, das früher als Negativpunkt für JavaScript gesehen wurde. Dadurch ist die Entwicklung mit JavaScript viel viel angenehmer geworden. Dieser Trend ist aber nicht erst seit diesen beiden Frameworks der Fall. Auch andere Frameworks erleichtern auf ihre Weise die Arbeit mit JavaScript. Die einen durch das Einführen von Patterns, die anderen durch das Anbieten von fertigen UI- und Layout-Komponenten.

Wir haben gesehen, dass auch bekannte Entwicklungspatterns wie MVVM oder MVC, unter anderem durch die betrachteten Frameworks, unterstützt werden und JavaScript somit nicht nur für Webentwickler interessanter geworden ist, sondern auch Desktopentwickler, die z.B. aus der WPF-Entwicklung kommen, ihren Spaß damit haben werden.

Wir haben gesehen, dass durch Testing Frameworks wie Karma, Jasmine, Mocha oder QUnit die JavaScript Entwicklung qualitativ hochwertiger und robuster wird.

Ich hoffe, es ist dadurch deutlich geworden, welches Potential in der Sprache steckt und das in Zukunft nicht nur im Web damit entwickelt wird 🙂

Part 4 – AngularJS 2/2

Im vorherigen Artikel haben wir schon begonnen uns mit AngularJS auseinander zu setzen. Wir haben die ersten Erfahrungen gemacht und festgestellt, dass es eine Menge zu Lernen gibt. Zu den neuen Konzepten der Services und der Möglichkeit der Dependency Injection, gibt es auch altbekannte Muster, wie das Data Binding und das Templating. Im Großen und Ganzen denke ich, dass der Aufwand überschaubar ist und dafür später durch eine flexible Anwendung belohnt wird.

Die Anwendung

Nachdem unsere todoApp im letzten Artikel definiert und auch das HTML-Grundgerüst erstellt wurde, wird es Zeit, sich um die eigentliche Implementierung zu kümmern. Beginnen wir mit den todoApp.services (app/js/services.js):

   1: var module = angular.module('todoApp.services', []);

   2: module.factory('todoService', function(){

   3:     var todos = {};

   4:     todos.get = function(){ 

   5:         return [

   6:             {description: "Müll raus bringen", done: false},

   7:             {description: "Kinokarten kaufen", done: false}

   8:         ];

   9:     };

  10:     return todos;

  11: });

Analog zu unserem todoApp-Modul erzeugen wir unser todoApp.services-Modul. Das todoApp.services-Modul besitzt keine Abhängigkeiten zu anderen Modulen, daher ist das Array für Abhängigkeiten leer.

Im erzeugten Service-Modul erzeugen wir mithilfe der factory(name,function/array) Funktion unseren todoService. Der todoService hat keine Abhängigkeiten zu anderen Modulen, somit übergeben wir kein Array, sondern einfach die Funktion, die den Service erzeugt. Alternativ hätte man auch ein Array, mit der Funktion als einziges Item, übergeben können. In AngularJS sind Services immer Singletons und werden per Lazy loading erzeugt.

Nachdem wir den todoService erstellt haben, wird es Zeit, unser todoApp.controllers-Modul zu erstellen (app/js/controllers.js):

   1: var module = angular.module('todoApp.controllers', []);

   2: module.controller('TodoController', 

   3:             ['$scope', 'todoService', function(scope, todoService, guid) {

   4:     scope.todos = todoService.get();

   5:     scope.query = "";

   6:     scope.newTodo = "";

   7:     scope.addNew = function(){

   8:         scope.todos.push({description: scope.newTodo, done: false});

   9:         scope.newTodo = "";

  10:     };

  11: }]);

  12: module.controller('AboutController', ['$scope',function(scope) {

  13:     scope.aboutMessage = "Kleine Anwendung zum verwalten von Todos";

  14: }]);

Wie man sieht, kommt man an Modulen nicht vorbei. Wir erzeugen das todoApp.controllers-Modul und nutzen die controller(name,function/array) Funktion des Moduls, um unseren TodoController und AboutController zu erstellen. Der erste Parameter ist der Name des Controllers. Der zweite Parameter ist die Funktion bzw. das Array, die bzw. das beim Erstellen des Controllers verwendet wird (Konstruktor).

Unser TodoController hat zwei Abhängigkeiten. Die eine Abhängigkeit ist unser gerade erstellter todoService. Die andere Abhängigkeit wird von Controllern so gut wie immer benötigt: $scope.

Mit dem $scope bekommen wir Zugriff auf den aktuellen Ausführungskontext des Controllers. $scope kann zum Beispiel in Callback-Funktionen eines Controllers verwendet werden, um Zugriff auf die Eigenschaften oder Funktionen des Controllers zu bekommen.

In unserer Konstruktor-Funktion werden alle Eigenschaften oder Funktionen, die später in der View zum Data Binding benötigt werden, dem scope hinzugefügt. Zusätzlich initialisieren wir hier die Daten. Dafür nutzen wir den todoService und rufen die Todo-Einträge ab. Diese weisen wir der todos Eigenschaft des TodoControllers zu.

Die query Eigenschaft dient der im vorherigen Beitrag erwähnten Filterfunktion. Dazu aber gleich noch mehr.

Der AboutController ist nur zu Demonstrationszwecken gedacht, um eine zweite Seite anzubieten. Daher hat dieser auch keine Abhängigkeiten und nur die eine statische Eigenschaft aboutMessage.

Nachdem wir nun unsere Controller haben, wird es Zeit, sich um die Views zu kümmern. Dafür erstellen wir zwei Views, auf die wir in unserer Navigation schon verwiesen haben.

Beginnen wir mit der ersten und wichtigsten View (app/partials/todos.html):

   1: <div id="todos-page" class="container-fluid">

   2:   <div class="row-fluid">

   3:       Filter: <input ng-model="query" placeholder="Filter die Einträge .."></input>

   4:       <ul class="todos">

   5:         <li ng-repeat="todo in todos |filter: query" class="">

   6:           <input type="checkbox" ng-model="todo.done" />

   7:           <span class="todo-{{todo.done}}">{{$index + 1}} - {{todo.description}}</span>

   8:         </li>

   9:       </ul>

  10:       <div class="input-append">

  11:           <input class="input-medium" type="text" ng-model="newTodo" placeholder="Neuer Todo Eintrag .." />

  12:           <button class="btn" ng-click="addNew()">Add</button>

  13:       </div>

  14:   </div>

  15: </div>

Hier sehen wir unsere Partial-View für die Darstellung der Todo-Einträge. Neben den bekannten HTML-Elementen und Attributen, haben wir auch hier wieder eine ganze Reihe von AngularJS spezifischen Attributen.

Beginnen wir mit ng-model:

ng-model wird verwendet um Two-Way-Binding zwischen der View und dem Model herzustellen. Mit Model meint man hier eine Eigenschaft des Controllers. Angewandt wird ng-model auch nur auf Eingabefelder wie input, select und textarea. Interessant zu wissen ist die Tatsache, dass AngularJS das jeweilige Model erstellt, sollte es nicht schon im Controller $scope enthalten sein. Allerdings finde ich es recht unübersichtlich, wenn auf einmal Eigenschaften des Controllers vom View erzeugt werden. Daher erzeuge ich für eine bessere Wartbarkeit immer alle Eigenschaften, die später in der View benutzt werden, im Controller.

Als Nächstes betrachten wir ng-repeat:

ng-repeat ist im Prinzip eine foreach-Schleife und erzeugt ab dem Element, auf dem ng-repeat eingesetzt wird, einen neuen Scope. Der neue Scope zeigt auf das aktuell in der Schleife befindliche selektierte Element der Liste. Von der Sache her ist das ähnlich zu dem foreach aus knockoutJS. Alle HTML-Elemente, die zwischen diese Schleife geschrieben werden, dienen auch hier als Template für das gerade selektierte Element der Liste.

Kommen wir nun zum Filter:

Wie im HTML-Snippet schon erkennbar, wurde hinter die foreach-Schleife per Pipe-Symbol ein Filter angehängt. Die Filter Funktion ist Bestandteil von AngularJS und filtert ein Array auf bestimmte Werte. Damit kann in unserer Anwendung mit einfachen Mitteln eine Filterung über die verfügbaren Todo-Einträge gemacht werden. Die Filterfunktion kann entweder einen String, ein Objekt oder eine Funktion als Filterkriterium nutzen. In unserem Fall ist es ein String. Nämlich der String, der in die query-Eigenschaft geschrieben wird.

Als Letztes möchte ich hier noch auf die curly brace {{ … }} Syntax eingehen. Das ist die Template Syntax, um dynamisch HTML zu erzeugen und zusätzlich ein One-Way-Binding zwischen Controller-Eigenschaften und HTML-Elementen herzustellen. Ändern sich hier die Werte der Controller-Eigenschaft, wird das HTML-Element automatisch mit dem aktuellen Wert aktualisiert. Diese Syntax kann quasi überall in der Seite eingesetzt werden. Damit lässt sich zum Beispiel auch das class-Attribut dynamisch ändern, sobald sich die done-Eigenschaft eines Todo-Eintrags ändert.

Als Nächstes erstellen wir noch schnell die About Seite (app/partials/about.html):

   1: <div>

   2:   <p>About Stuff</p>

   3:   <div>

   4:       {{aboutMessage}}

   5:   </div>

   6: </div>

Hier gibt es nix neues, daher gehe ich gleich weiter im Text und wir schauen uns die fertige Anwendung mal im Browser an 🙂

Resultat

todolist   about

Im Ergebnis kein Unterschied zu der Version mit knockoutJS (abgesehen von dem CSS und der Navigation). Allerdings haben wir durch die modulare Aufteilung des Service, der Controller und der Views die Chance bekommen, auch wesentlich komplexere Anwendungen zu entwerfen, die sich am Ende immer noch gut warten lassen.

Ausblick und Fazit

Wir haben bis jetzt einen groben Einblick in die Möglichkeiten von AngularJS bekommen und haben auch ein Gefühl dafür bekommen, z.B. mehrere Views einzubinden. Im nächsten Beitrag werde ich noch auf das Testing eingehen, was durch den modularen Aufbau der Anwendung ermöglicht bzw. erleichtert wurde.

Stay tuned 😉

Part 3 – AngularJS 1/2

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 🙂

Part 2 – knockoutJS

Im letzten Artikel haben wir eine kleine, schnelle und möglicherweise auch nicht ganz vollständige Geschichte über JavaScript und die ersten aufkommenden Frameworks gehört 🙂 In den kommenden Beiträgen geht es darum, die im letzten Vortrag genannten Frameworks knockoutJS und AngularJS ein wenig zu beleuchten und aufzuzeigen, welche Vorteile diese Frameworks bieten. Beginnen möchte ich in dem heutigen Beitrag mit dem Framework, welches einen wortwörtlich “umhaut” 😉

 

KnockoutJS

knockoutJS  ist ein ziemlich kleines JavaScript Framework mit einer Größe von 40kb und bietet im Wesentlichen vier Kernfunktionalitäten:

  • Declarative Bindings
  • Automatic UI Refresh
  • Dependency Tracking
  • Templating

Das bedeutet im Wesentlichen, dass dem Einsatz des Model-View-ViewModel Patterns und Two-Way-Data Binding nichts mehr im Wege steht.

Das Wichtigste, was uns knockoutJS bietet, sind Observables. Observables sind quasi der Kleber zwischen unseren JavaScript-Objekten (ViewModels) und den HTML-Elementen (View). Über eine definierte Syntax werden die Eigenschaften des ViewModels an die HTML-Elemente gebunden (Data Binding).

Gibt es Änderungen in einer Eigenschaft des ViewModels, werden diese sofort im View aktualisiert. Umgekehrt funktioniert das genauso. Ändert sich ein Eintrag z.B. in einem Textfeld, wird das direkt in die Eigenschaft des ViewModels geschrieben. Früher mussten händisch Eventhandler erstellt werden, um diesen Mechanismus abzubilden. Heute übernehmen diese Arbeit Frameworks wie knockoutJS für uns.

Am besten machen wir jetzt ein kleines Beispiel und bringen auch endlich ein wenig Code ins Spiel. Wie heutzutage üblich, machen wir uns ein kleines “Hello World”-Programm in Form einer kleinen Todo-Liste, um die Konzepte hinter knockoutJS besser zu verstehen.

Praxis

Unsere Todo-App soll eine Liste enthalten und dort sämtliche erfassten Todos darstellen. Es gibt ein Textfeld und einen Button zum Hinzufügen neuer Todo-Einträge. Die einzelnen Todos können über eine Checkbox auf “Fertig” und “Noch zu erledigen” gesetzt werden.

Schauen wir uns zunächst das ViewModel an:

   1: function TodoListVM(){

   2:     var self = this;

   3:     self.todos = ko.observableArray([

   4:         {

   5:             description: "Müll raus bringen",

   6:             done: ko.observable( false )

   7:         },{

   8:             description: "Kinokarten bestellen",

   9:             done: ko.observable( false )

  10:         }

  11:     ]);

  12:     self.newTodo = ko.observable();

  13:     self.add = function(){

  14:         self.todos.push({

  15:             description: self.newTodo(),

  16:             done: ko.observable( false )

  17:         });

  18:     }

  19: }

  20: ko.applyBindings(new TodoListVM());

Hier haben wir unser ViewModel für die Todo-Liste. Es gibt ein Feld todos zum Halten der Todo-Einträge. Initial fügen wir zwei Einträge hinzu. Des Weiteren gibt es ein Feld newTodo, welches den Beschreibungstext für einen neuen Todo-Eintrag enthält. Als Letztes haben wir noch eine Funktion, die aufgerufen wird, um Todo-Einträge hinzuzufügen.

Der Aufruf ko.applyBindings(..) am Ende sorgt dafür, dass unser ViewModel an das DOM der Seite gebunden wird. Dieser Aufruf gießt quasi den Kleber zwischen den HTML-Elementen und unserem ViewModel in die Seite.

Die Eigenschaft todos unseres ViewModels ist ein observableArray(..). Durch diese Zuweisung wird sichergestellt, dass beim Hinzufügen eines weiteren Todo-Eintrags automatisch das View aktualisiert wird.

Als Nächstes haben wir mit der Eigenschaft newTodo noch ein “einfaches” observable. Diese Eigenschaft wird gefüllt, sobald wir in dem gebundenen Textfeld einen Text eingeben. Ausgelesen wird newTodo, wenn die Funktion add() aufgerufen wird. Das passiert sobald wir auf den Add-Button drücken.

Nachdem unser ViewModel steht, kommen wir nun zur HTML-View und dem Data Binding:

   1: <div data-bind="foreach: todos">

   2:     <div>

   3:         <input type="checkbox" data-bind="checked: done" />

   4:         <span data-bind="text: description, css: {todoDone: done}"></span>

   5:     </div>

   6: </div>

   7: <input type="text" data-bind="value: newTodo" />

   8: <button data-bind="click: add">Add</button>

Hier sehen wir unsere HTML-View. Auf den ersten Blick sehen wir hier divs, spans und input Felder. Ganz normale HTML-Tags. Des Weiteren sehen wir aber auch knockoutJS spezifische Attribute wie z.B. data-bind. Diese Attribute werden benutzt, um das Binding zwischen den Eigenschaften des ViewModels auf die entsprechenden HTML-Element herzustellen.

Sieht man genauer hin, hat man in dem data-bind Attribut die Möglichkeit, Ausdrücke zu erzeugen. Diese Ausdrücke erklären dem Binding, was gemacht werden soll.

Beispiel: <input type=”text” data-bind=”value: newTodo” />

Hier wird folgendes beschrieben: Nimm aus dem ViewModel die Eigenschaft newTodo und binde diese an das input-Element an dessen value-Attribut.

Auch interessant ist das Binding für die todos. Hier sagen wir dem Binding, dass es bitte mit jedem Todo-Item aus der todos-Eigenschaft des ViewModels etwas Bestimmtes anstellen soll. Das, was er tun soll, wird einfach zwischen das HTML-Element geschrieben und ist im Grunde nichts anderes als ein Template, welches zum Rendern jedes Todo-Eintrags genutzt wird.

Das Data Binding funktioniert sowohl für Daten zum Eingeben als auch für Style-Attribute, CSS-Klassen und alles was es sonst noch an Attributen gibt. Das Limit ist an dieser Stelle, wie fast überall, die Fantasie 😉

Nun ist es Zeit, die Früchte unserer Arbeit zu betrachten:

knockoutScreen

Hier sehen wir das Ergebnis. Ich habe hier schon einen neuen Todo-Eintrag erstellt und mit Add der Liste hinzugefügt. Zudem wurde “Müll raus bringen” als “erledigt” markiert.

Fazit und Ausblick

An dieser Stelle möchte ich nochmal darauf hinweisen, dass für jede Aktualisierung der View oder auch des ViewModels keinerlei Eventhandler geschrieben wurde. Die Zuweisung von Werten aus der View zum ViewModel oder die Aktualisierung der UI musste nicht manuell über Eventhandler realisiert werden! Diesen “Schmerz” nimmt einem knockoutJS ab, sodass man sich auf die Erstellung der UIs bzw. der Logik konzentrieren kann.

Zudem hat knockoutJS keine Abhängigkeiten zu anderen Frameworks, unterstützt (laut Webseite) sogar den IE ab 6+ und kann trotzdem gut zusammen mit jQuery verwendet werden.

Wer das Beispiel mal ausprobieren will, kann dies gerne auf jsFiddle tun.

Im nächsten Beitrag betrachten wir AngularJS. Ein weiteres spannendes Framework, welches ebenfalls Konzepte wie Data Binding unterstützt, aber zusätzlich noch weitere nette Features mitbringt.

Stay tuned! 🙂

Part 1 – Yet another Story about JavaScript

JavaScript – eine Sprache die entstanden ist, um das Web dynamischer zu machen. Ursprünglich für kleine Aufgaben gedacht, hat sie sich heute zu einer universalen Allzweckwaffe entwickelt. Schon seit einer ganzen Weile läuft JavaScript nicht mehr nur im Browser, sondern hat durch Frameworks wie NodeJS den Sprung auf die Server geschafft und setzt dort ihren Siegeszug fort.

JavaScript ist nicht mehr wegzudenken. Ich für meinen Teil finde das super, weil ich die Sprache ziemlich gut finde. Das liegt zum einen daran, dass JavaScript leicht zu erlenen ist – es gibt nicht hunderte Konstrukte, die man kennen muss, um die Sprache zu beherrschen. Zum anderen liegt das an der Flexibilität, die mir die Sprache bietet.

Frameworks – The Beginning

Von der Sprache her ist alles super. Leider gab es in nicht so ferner Vergangenheit diverse Probleme mit der Kompatibilität der Sprache in den einzelnen Browsern. Vor allem was den Zugriff auf das DOM betraf. Das hat die Arbeit mit JavaScript erheblich erschwert, da Funktionen, die in dem einen Browser geklappt haben, in dem nächsten nicht mehr funktionierten. Die Zeiten sind aber mit dem Auftauchen von Frameworks wie jQuery, ExtJS, Dojo und vielen anderen beliebten Frameworks glücklicherweise vorbei.

All diese Frameworks erleichterten den Umgang mit unterschiedlichen Browsern und brachten die Entwicklung mit JavaScript auf ein neues Level. Für mich sind das die Urgesteine der JavaScript Frameworks 🙂

Frameworks – Next Generation

Mittlerweile gibt es eine neue Generation von Frameworks, die beliebte Patterns wie MVC oder auch MVVM unterstützen. Diese Patterns werden gerne in der Entwicklung von Webanwendungen genutzt und sind auch stark in der Entwicklung von WPF oder Windows Store Apps verankert.

Mit dem Erscheinen solcher Frameworks – wie BackboneJS, knockoutJS, AngularJS und EmberJS – wird der Entwicklung mit JavaScript erneut ein Schub nach vorne gegeben. Der große Vorteil dieser Frameworks ist für mich das “Abholen” der Desktop-Entwickler, die mit dem MVVM-Pattern schon vertraut sind und ihr Wissen somit fast eins-zu-eins übernehmen können.

Ein gutes Beispiel für dieses “Abholen” ist das Framework knockoutJS, das sich stark an die Entwicklung von WPF-Anwendungen, mit dessen MVVM-Pattern und Data Binding Mechanismus, anlehnt und mit dem sich der “Desktop Entwickler” schon fast wie zu Hause fühlt 🙂

Ein großer Vorteil der Frameworks ist das schon kurz erwähnte Data Binding, das in den meisten Fällen in zwei Richtungen erfolgt. Dadurch ist es nicht mehr notwendig Eventhandler zu erstellen, die auf onchange/onleave usw. hören, um dort die eingegebenen Werte in entsprechende JavaScript Objekte zu übergeben. Diese Aufgabe übernimmt das ausgewählte Framework.

Testing

Neben den ganzen tollen Features, die einem das Entwickeln erleichtern, gibt es einen weiteren wichtigen Aspekt, der immer mehr Tragweite bekommt. Das Testen. Dadurch, dass JavaScript nicht mehr nur partiell Verwendung findet, um mal eben ein paar Daten per AJAX zu laden oder Bilder in einer Slideshow darzustellen, sondern im Client mehr und mehr komplexe Anwendungen entstehen, muss hier natürlich sichergestellt werden, dass der Code funktioniert.

Ein bekanntes Framework, das sich mit dem Testen auseinander setzt, ist QUnit. Dabei schreibt man seine Unit-Tests in einer separaten Seite und führt diese durch einen Aufruf der Seite aus.

Ein anderes Framework ist Jasmin, das den BDD-Weg verfolgt. Dieses Framework wird auch für AngularJS empfohlen und bietet in Zusammenarbeit mit Karma, neben den klassischen Unit-Tests, auch die Möglichkeit End-To-End Tests zu machen. Im Prinzip sind das automatisierte UI Tests. Dabei können bestimmte Seiten geladen, Felder mit Werten gefüllt und danach überprüft werden, ob sich die richtigen Folgeschritte ergeben haben.

Ausblick und Fazit

Dies war eine – aus meiner Sicht – kleine Geschichte zur Entwicklung und dem Beginn der Revolution von JavaScript durch diverse Frameworks.

Auch in Zukunft führt – meiner Meinung nach – kein Weg an JavaScript vorbei! Durch die kontinuierliche Entwicklung der diversen Frameworks wird JavaScript mehr und mehr zu einer professionellen Sprache, mit der sich komplexe und vor allem auch testbare Anwendungen erstellen lassen. Das Einsatzgebiet von JavaScript steigt ständig.

Wie zu Beginn erwähnt, läuft JavaScript nicht mehr nur im Browser, sondern dank NodeJS auch auf dem Server. Cordova (ehemals Phonegap) erlaubt die Erstellung von mobilen Anwendungen, die vollständig auf dem Gerät laufen. Seit der Einführung von Windows 8 ist es möglich, Windows Store Apps ebenfalls in JavaScript zu erstellen. Mit FirefoxOS hat Mozilla ein Handy Betriebssystem auf den Markt gebracht, welches vollständig und nativ auf HTML5, JavaScript und CSS setzt. Mit asm.js ist es möglich, JavaScript fast so schnell auszuführen wie nativen Code. Es passiert also eine ganze Menge in diesem Bereich und es wird noch einiges auf uns zukommen.

In den kommenden Beiträgen gehe ich auf die für mich gerade wichtigsten Frameworks knockoutJS und AngularJS ein.

Stay tuned! 😉

Mein neuer Freund Sammy

div class=”articleAbstract”>In der Webentwicklung wandert immer mehr Logik vom Server in den Client. Das hat unter anderem den Vorteil, dass zum Beispiel Validierungen direkt auf dem Client gemacht werden können und damit keine langen Antwortzeiten vom Server entstehen. Das schont a) die Serverressourcen und b) freut sich der Benutzer, dass er sofort Feedback bekommt und nicht etwa erst dann, wenn er das Formular mit 20 Feldern ausgefüllt hat 😉

Das Beispiel mit der Validierung zeigt, dass es wichtig ist auch im Browser eine kurze Reaktionszeit der UI zu haben. Also hat man begonnen nicht nur Validierung im Browser, sondern ganze Anwendungslogiken abzubilden. Für den Benutzer sieht es dann so aus, als würde er sich nur auf einer Seite befinden. Im Zuge sog. Single-Page Application kommt man schnell an den Punkt, an dem man eine Navigationsmöglichkeit von “Seite zu Seite” benötigt. Im klassischen ASP.NET MVC hat man einfach einen Link/Controller aufgerufen, der auf eine neue Seite gesprungen ist. Dadurch wurde immer ein Request an den Server durchgeführt, der die neue Seite gerendert und zurückgeliefert hat.
Bei der Single Page Application hat man allerdings alle “Seiten” in einer HTML-Seite. Die Inhalte der “Seiten” sind in der Regel in div-Elemente gekapselt und werden über einen Mechanismus sichtbar und/oder unsichtbar geschaltet.
Über dieses Schalten der Sichtbarkeiten kann man nun seine Navigation abbilden. Die Frage ist nur wie?

Sammy to the Rescue

Sammy ist ein kleines JavaScript Framework, das den URL-Hash (#) benutzt, um darüber eine Navigation abzubilden. Jeder der mit dem MVC-Framework arbeitet ist bestimmt schon mal über das Konzept der Routen gestoßen. Dieses Konzept findet sich auch in Sammy wieder. Mit Sammy kann man Routen und dazugehörige Aktionen definieren. Wird eine URL aufgerufen, die mit einer konfigurierten Route übereinstimmt, wird die entsprechende Aktion aufgerufen. Beispiel:

1: this.get('#:page', function () { 

2:     var page = this.params.page; 

3:     alert(page); 

4: });

Ruft man im Browser nun folgende URL http://www.mypage.de/#About auf, so trifft diese unsere Route und es wird “About” in einer Message-Box ausgegeben.
Komplettes Beispiel:

1: // Client-side routes 

2: Sammy(function () { 

3:     this.get('#:page', function () { 

4:         var page = this.params.page; 

5:         alert(page); 

6:         // Hide all divs 

7:         // Show div that hold the Page
                        Content 

8:     }); 

9:     // Start initial Page 

10:     this.get('', function () { 

11:         this.app.runRoute('get', '#Startpage'); 

12:     }); 

13: }).run(); 

Wie man sieht, nutzt Sammy das Hash-Tag (#), um seine Navigation zu verwalten. Damit ist es auch möglich im Browser den Vor- und Zurückbutton zu verwenden. Der Browser navigiert dadurch nicht auf die zuletzt besuchte Internetseite, sondern auf die zuletzt sichtbare “Seite” in der Single-Page Application.

Diese Methodik ist auch praktisch bei WebApps. Beim Drücken der Zurücktaste auf dem Handy wird damit auch hier in der Single-Page Application zurückgeblättert und nicht die zuletzt besuchte Internet Seite geladen.

Fazit

Wer demnächst also eine Single Page Application entwickeln möchte, sollte unbedingt mal einen Blick
auf Sammy werfen. Eine gute Einstiegshilfe bietet im Übrigen die knockoutJS Tutorial Seite.

Quellen: http://sammyjs.org/http://learn.knockoutjs.com/#/?tutorial=webmail