May 6, 2014
Ein Blick in die Karten
Seit dem Erscheinen der Version 1.0 im Juni 2012 entwickeln wir bei WeigleWilczek sowohl interne als auch Kunden-Projekte mit AngularJS. In dieser Zeit haben wir viel über AngularJS und die Art und Weise mit AngularJS Webanwendungen zu entwickeln gelernt. Mit diesem und den folgenden Posts möchte ich einen Einblick in unseren Entwickler-Alltag, die von uns verwendeten Tools und unsere Erfahrungen mit AngularJS geben.
Editor oder IDE
Grundsätzlich lässt sich für die Entwicklung einer AngularJS Anwendung jeder Editor verwenden. Syntax-Highlighting für die verwendeten Sprachen JavaScript, HTML und CSS sollten dabei aber das Minimum an Ausstattung sein.
Für viele Leute, die bisher hauptsächlich mit statisch typisierten Sprachen, die kompiliert werden, gearbeitet haben stellt die dynamische Typisierung und die Flexibilität, die JavaScript mit sich bringt, am Anfang eine große Hürde da. Abhilfe kann hier JSLint bzw. der Fork JSHint schaffen. JSLint ist ein Programm zur statischen Code Analyse und kann viele Fehler, die ein Compiler einer statisch typisierten Sprache bemerken würde, ebenfalls bereits zur Entwicklungszeit erkennen.
Je größer ein Projekt wird, desto schwieriger wird es im Code den Überblick zu behalten und in ihm zu navigieren. Hier kann wie in der statisch typisierten Welt eine gute IDE helfen. Moderne IDEs können auch bei JavaScript viele gute Vorschläge beim Navigieren oder bei der Autovervollständigung anbieten. Diese sind nur immer mit etwas Vorsicht zu genießen, da die IDE eben nicht 100% sicher sein kann, ob sie das richtige verschlägt. Ich verwende am liebsten WebStorm bzw. den großen Bruder IntelliJ. Achtung: IntelliJ in der kostenlosen Community Edition hat weniger Web-Funktionen als das kostenpflichtige WebStorm, da es eigentlich eine Java IDE ist. IntelliJ Ultimate ist beides: Web- und Java-IDE. Gerade was die bereits erwähnte Punkte Navigation im Code und Autovervollständigung angeht hat IntelliJ sehr viel zu bieten.
Einer weiterer der großen Vorteile von WebStorm/IntelliJ ist das AngularJS Plugin. Durch dieses kann die IDE im HTML Code für die von AngularJS mitgelieferten Direktiven Autocompletion anbieten. Zusätzlich kann man bei Expressions wie bei ng-click direkt zur Definition von Funktionen im Scope springen. Im JavaScript Code steht Autocompletion für alle AngularJS Funktionen zur Verfügung.
Alles in allem lohnt es sich meiner Meinung nach auf eine IDE statt auf einen Text-Editor zu setzen und sich ggf. auch in eine neue IDE einzuarbeiten bzw. sie anzuschaffen.
Aktuelle Empfehlung: WebStorm bzw. IntelliJ
Dependency Management
Für die meisten Sprachen bzw. Eco-Systeme wie Python, Ruby, Java, … gibt es Tools, die Abhängigkeiten zu anderen Bibliotheken verwalten. Der Funktionsumfang ist dabei unterschiedlich. Maven in der Java-Welt sorgt z.B. nicht nur für das Herunterladen der angegeben Bibliothek in der angegebenen Version, sondern auch für das automatische Hinzufügen der Klassen der Bibliothek zum Classpath. Andere Tools sorgen nur dafür, dass die Bibliothek runter geladen wird, binden diese aber nicht automatisch ein.
Im Web-Bereich wurden Abhängigkeiten bisher fast immer von Hand gepflegt. Man lädt eine Version von der Website des Projekts runter, legt sie in einem Verzeichnis eventuell unterhalb einer gewissen Ordner-Struktur ab und bindet die Datei(en) in die index.html ein. Das hat vor allem den Nachteil, dass man sich auch um die sogenannten transitiven Abhängigkeiten, also die Abhängigkeiten der Abhängigkeiten selbst kümmern muss und Konflikte bei kompatiblen Versionen so nur recht schwierig zu bemerken sind.
Mit bower hat das zum Glück ein Ende. Abhängigkeiten werden bei bower in einer Datei bower.json gespeichert und per Kommandozeile mit bower install
heruntergeladen. Dabei können wie im Beispiel unten zu sehen variable Versionsnummern verwendet werden. Bower erkennt Konflikte und fragt welche Version verwendet werden soll. Um das Einbinden der im Paket enthaltenen Dateien muss man sich selbst kümmern. Ein Paket muss dabei nicht zwangsläufig JavaScript Code enthalten, sondern kann auch beispielsweise CSS oder Bilder mit sich bringen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "name": "w11k-select", "version": "0.3.4", "dependencies": { "angular": "1.2.x", "w11k-dropdownToggle": "0.1.x", "angular-bindonce": "0.3.x" }, "devDependencies": { "font-awesome": "4.0.x", "bootstrap": "3.1.0", "es5-shim": "2.3.0" } } |
Bower lädt die Pakete aus ihren jeweiligen Repositories runter. Es gibt also kein zentrales Repository für alle bekannten Pakete. Das hat den großen Vorteil, dass auch kein zentrales Repository ausfallen kann, wie kürzlich bei NPM geschehen. Leider ist bower aber etwas anfällig für Verbindungsabbrüche. Das ist besonders ärgerlich, wenn man bower auf einem CI-Server einsetzt und der Build fehlschlägt nur weil bower ein Verbindungsproblem hatte. Beim nächsten Aufruf des Builds funktioniert dann meistens alles wieder reibungslos, sofern nicht wirklich ein Problem mit Abhängigkeiten vorliegt.
Um das Problem zumindest temporär zu umgehen, kann man bower anweisen Pakete nur aus dem lokalen Cache zu installieren und diesen nicht über das Internet zu validieren. Dafür einfach den Parameter -o mitgeben, also z.B. bower install -o
.
Eine Stolperfalle hat bower noch zu bieten: bower install installiert nur noch nicht vorhandene Pakete. Besteht eine Abhängigkeit zu einem Paket in der Version 1.2.x und es ist momentan Version 1.2.15 installiert, so wird bei bower install nicht die neuere Version 1.2.16 heruntergeladen, da schon eine Version des Pakets installiert ist. Ruft man bower update
werden auch für bereits installierte Pakete neue Versionen gesucht. In früheren Bower Versionen hat bower update aber keine neuen Abhängigkeiten installiert. Das scheint jetzt behoben zu sein, so dass eigentlich bower update ausreichen sollte.
bower install && bower update
aufrufen.Für die direkten Abhängigkeiten eines Projekts hat es sich bei uns bewährt auf variable Versionsnummern zu verzichten und immer eine vollständige SemVer-Version anzugeben. Wenn das Projekt (die Abhängigkeit) nicht mit SemVer und Tags arbeitet kann man bei bower auch einen git-Commit-Hash angeben. Auch wenn SemVer eigentlich vorgibt, dass Patch-Versionen kompatibel zueinander sein sollen, kommt es immer mal wieder vor, dass die eigene Anwendung mit dem einen Patch-Level funktioniert und mit dem anderen nicht. Damit so etwas nicht erst beim erstellen des Releases auffällt, sollte man feste Versionen eintragen und das Update explizit von hand durchführen.
Manchmal kann es auch hilfreich sein eine eigentlich transitive Abhängigkeit als direkte anzugeben um eine fixe Versionsnummer angeben zu können wenn in der eigentlichen Abhängigkeit eine variable Version angegeben ist.
Fixe Versionen bei Entwicklung einer Anwendung
Variables Patch-Level bei Entwicklung einer Bibliothek
Module
Jetzt wissen wir, wie wir Abhängigkeiten verwalten wollen. Aber welche haben wir denn in der Regel so? Bei unseren bisherigen Projekten haben sich einige Bibliotheken und AngularJS-Module als sehr nützlich bis unersetzlich erwiesen. In unserer bower.json steht daher fast immer:
- AngularJS 1.2 im möglichst aktuellen Patch
- AngularUI Router als mächtige und flexiblere Alternative zum eigenen Routing von AngularJS (ngRoute)
- angular-translate für Internationalisierung
- Twitter Bootstrap für das Styling und AngularUI Bootstrap für einige UI Komponenten
- lodash für mehr funktionale Programmierung und moment.js für den leichteren Umgang mit Datums- und Zeit-Werten
- angular-bindonce für etwas mehr Performance bei großen Datenmengen
Internet Explorer 8 und Internet Explorer 9
Muss man IE8 und IE9 unterstützen sollte man einige Polyfills einsetzen um fehlendes Verhalten und APIs nach zurüsten. Die wichtigsten sind:
- respond für CSS3 Media Queries
- es5-shim für EcmaScript 5 Methoden
- console-polyfill zum Loggen auf die Konsole über das console Objekt
- json3 für JSON Support
Mithilfe dieser Polyfills und etwas Obacht bei der Verwendung von AngularJS kann man Anwendungen entwickeln, die auch in älteren IE Versionen gut funktionieren. Bei AngularJS ist besonders darauf zu achten, dass man Direktiven nicht als Tags sondern als Attribute verwendet. In der AngularJS Dokumentation gibt es eine Seite zur Verwendung in älteren IE Versionen. Allgemein sollte beachtet werden, dass IE8 und auch IE9 eine sehr langsame JavaScript Engine haben uns so die Verarbeitung größerer Datenmengen zu einem Problem werden kann.
<div directive></div>
statt <directive></directive>
verwenden!