Transcript

Große Applikationen mit

WHO AM I?

• Sebastian Springer

• aus München

• arbeite bei Mayflower

• https://github.com/sspringer82

• @basti_springer

• Consultant, Trainer, Autor

Konventionen

Wenn ihr heute nur einen Punkt mitnehmt, dann:

Haltet euren Code und eure Struktur einheitlich.

Was ist eine große Applikation?

K. Bangwa / pixelio.de

Großer Funktionsumfang

Grundsätzliches

Bernd Kasper / pixelio.de

Sprechender Code

Sprechender Code

angular.module('myApp', []) .controller('MyCtrl', MyCtrl);function MyCtrl() { var vm = this; vm.onSaveButtonClicked = onSaveButtonClicked; function onSaveButtonClicked() {}}

Sprechender Code

Was ist, wenn ein Funktionsname zu lange wird? Dann macht die Funktion wahrscheinlich zu viel und sollte

aufgeteilt werden.

Komplexität

Tim Reckmann / pixelio.de

Komplexität

Applikation in kleinere, weniger komplexe Einheiten unter-teilen und diese zu einem Komplettsystem integrieren.

Komplexitätvm.start = function() { vm.status = "started"; stopInterval = $interval(function () { var data = readerDataFactory.getPart(); if (data) { vm.part = data; } else { vm.part = ""; vm.status = "stopped"; $interval.cancel(stopInterval); stopInterval = null; vm.text = readerDataFactory.getText(); } }, 1000 * 60 / readerDataFactory.getWPM());};

vm.stop = function() { vm.status = "stopped"; $interval.cancel(stopInterval); stopInterval = null; };

Styleguide

Styleguide

Der Quellcode soll überall gleich aussehen. Dem Quellcode soll man nicht ansehen, wann er von wem mit

welcher Laune geschrieben wurde.

Ein guter Start ist: https://github.com/johnpapa/angular-styleguide

Werkzeuge

Gila Hanssen / pixelio.de

Entwicklungsumgebung

Entwicklungsumgebung

Autocompletion: Variablen und Konstrukte vervollständigen. Hilfestellung bieten.

Highlighting: Visuelle Abgrenzung zwischen verschiedenen Konstrukten für erhöhte Lesbarkeit.

Linting: Anti-Pattern-Erkennung, um Fehler zu vermeiden.

Angular Plugins für WebStorm, Eclipse,…

Entwicklungsumgebung

Paketverwaltung

Rainer Sturm / pixelio.de

Paketverwaltung

Installation externer Bibliotheken. Versionsverwaltung und Überblick über installierte Bibliotheken. Auflösung von

Abhängigkeiten.

Paketmanager: bower, npm, spmjs,…

Paketverwaltung

$ bower install angular bower angular#* cached git://github.com/angular/bower-angular.git#1.4.0 bower angular#* validate 1.4.0 against git://github.com/angular/bower-angular.git#* bower angular#~1.4.0 install angular#1.4.0

angular#1.4.0 bower_components/angular

Yeoman

Scaffolding für Webprojekte. Bietet Generatoren, um schnell häufig benötigte Strukturen im Projekt zu schaffen.

Yeoman

npm install -g yo

npm install -g generator-angular

yo angular myApp

yo angular:controller account

YeomanProblem:

Aber unsere Applikation ist etwas ganz besonderes und die bestehenden Generatoren passen für uns nicht. Wir müssen

jede Struktur nochmal anfassen und umbauen.

Lösung:

Eigene Generatoren erstellen. Guide: http://yeoman.io/authoring/

Struktur

Initiative Echte Soziale Marktwirtschaft IESM / pixelio.de

Struktur

Wie baut man eine Applikation auf, damit sie wartbar und erweiterbar bleibt?

Wo findet man bestimmte Komponenten? Wie werden die Verzeichnisse strukturiert?

Wie heißen die Dateien?

Ein Verzeichnis pro Feature

Ein Verzeichnis pro Feature

Features unabhängig halten. Wiederverwendbarkeit in anderen Applikationen erreichen. Klares Namespacing

einführen.

Ein Verzeichnis für shared content

Ein Verzeichnis für shared content

Gemeinsam genutzte Komponenten liegen in einer eigenen Hierarchie. Abhängigkeiten zwischen Modulen werden

vermieden.

Eine Komponente pro Datei

Eine Komponente pro Datei

Controller, Services, Direktiven, … erhalten jeweils ihre eigene Datei.

Single Responsibility - eine Datei ist nur für einen Zweck da. Zuordnung von Zuständigkeiten ist einfach. Das Auffinden

von Quellcode ist schneller.

Eine Komponente pro Datei

angular.module('user.controller.login', []) .controller('user.loginController', LoginController); function LoginController() { }

Einheitliche sprechende Benennung

Einheitliche sprechende Benennung

Die Namen von Dateien und deren Speicherort folgt einem konsistenten Schema.

Das Lokalisieren von Komponenten wird einfacher.

/user/login.controller.js

Konfiguration auslagern

Konfiguration auslagern

Die Konfiguration eines Moduls liegt in einer eigenen Datei innerhalb des Moduls.

Die Konfiguration erfolgt an einer zentralen Stelle und nicht über die Komponenten verteilt.

Konfiguration auslagern

angular.module('addressBook.translation', ['pascalprecht.translate']) .config(['$translateProvider', function ($translateProvider) { $translateProvider.translations('de', de); $translateProvider.translations('en', en); $translateProvider.preferredLanguage('en'); }]);

Struktur flach halten

Struktur flach halten

Die Verzeichnisstruktur sollte initial möglichst flach sein. Bei Bedarf (viele Dateien pro Verzeichnis) tiefer werden.

Routing

Erich Westendarp / pixelio.de

Routing

Navigation zwischen verschiedenen States in der Applikation.

States arbeiten mit Views, die benannt sein können. Optional können Controller hinzugefügt werden.

Implementierungen: - ngRoute - angular-ui-router

Routing - Konfigurationangular.module('adress', ['ui.router']) .config(ConfigFn);ConfigFn.$inject = ['$stateProvider', '$urlRouterProvider']; function ConfigFn ($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise('/list'); $stateProvider.state('list', { url: '/list', template: tplList, controller: 'listCtrl' });}

Routing - advancedMehrere benannte Views

<body> <div ui-view="content"></div> <div ui-view="info"></div> </body>

$stateProvider.state('list', { url: '/list', views: { content: { template: tplListContent, controller: 'listContentCtrl' }, info: { template: tplListInfo, controller: 'listInfoCtrl' } }});

Routing - advanced

<div ui-view></div> <a ui-sref="state1">State 1</a>

<a ui-sref="state1.list">Show List</a> <div ui-view></div>

index.html

state1.html

state1.list.html<ul> <li ng-repeat="item in items">{{ item }}</li> </ul>

Nested Views

Routing - advancedNested Views

$stateProvider .state('state1', { url: "/state1", templateUrl: "partials/state1.html" }) .state('state1.list', { url: "/list", templateUrl: "partials/state1.list.html", controller: function($scope) { $scope.items = ["A", "List", "Of", "Items"]; } });

Routing - new Router

Redesignter Router. Folgeversion von ngRoute. Aktuell noch nicht stabil. Ist aber für 1.4 und 2 vorgesehen.

DRYDon’t Repeat Yourself

DRY

Sobald eine Routine zum zweiten Mal implementiert wird, sollte sie in einen eigenständigen Service ausgelagert

werden. Diese Services können abstrahiert und durch

unterschiedliche Konfiguration an mehreren Stellen wiederverwendet werden.

DRY

Nützliche Helper können in einer eigenen Library gesammelt werden.

z.B. Logger

NIHNot Invented Here

NIH

Bevor man etwas selbst implementiert, sollte man zuerst prüfen, ob es für diese Problemstellung nicht schon eine

Lösung gibt.

Beispiele: - REST-Kommunikation: ngResource, restangular - Websocket: angular-websocket, ng-websocket - localstorage: angular-local-storage - Routing: ngRoute, ui-router - Translation: ngTranslate

Dokumentation

René Golembewski / pixelio.de

Dokumentation

Es muss nicht jede Funktion dokumentiert werden (z.B. Callbacks)

Lieber sprechenden Code schreiben.

Dokumentation

Schnittstellen zwischen Teams/Projekten müssen dokumentiert und AKTUELL gehalten werden.

Dokumentation

Angular selbst verwendet jsdoc bzw. ngdoc und Dgeni zum Parsen.

Weitere Infos: https://github.com/angular/angular.js/wiki/Writing-AngularJS-

Documentation

Dokumentation

/** * @name trim * @private * * @description * trim polyfill * * @returns {string} The string stripped of whitespace from both ends */var trim = function() { return this.toString().replace(/^\s+|\s+$/g, '');};

Typescript

Zur Dokumentation von Schnittstellen kann zusätzlich Typescript eingesetzt werden.

Möglichkeit zur Beschreibung von Signaturen, Klassen und Interfaces.

Code

Corinna Dumat / pixelio.de

Struktur vs. Implementierung

Struktur vs. Implementierung

Trennung der konkreten Implementierung und der Struktur. Rückgabe/Bindables getrennt von den

Funktionsdefinitionen.

Struktur vs. Implementierungfunction ListCtrl() { var vm = this; vm.onEdit = onEdit; vm.onDelete = onDelete; function onEdit(id) {…} function onDelete(id) {…} }

function ItemService() { return { fetch: fetch, save: save }; function fetch() {...} function save() {...}}

Controller:

Factory:

init-Funktion

init-Funktion

Sämtliche Logik, die in einem Controller zur Initialisierung direkt ausgeführt wird, wird in einer Funktion gekapselt. So sieht man auf einen Blick, was passiert, wenn dieser

Controller erstellt wird.

init-Funktion

function ListCtrl(itemStore) { var vm = this; vm.items = []; function init() { itemStore.fetch().then(function(items) { vm.items = items; }); } init();}

Logik in Services

Logik in Services

Controller sollten nicht zu viel Logik beinhalten, da sie dadurch unleserlich werden. Umfangreichere Logik in

Services auslagern.

Ein Controller pro View

Ein Controller pro View

Mehrere Views sollten sich nicht einen Controller teilen. Dies macht es meistens erforderlich, Logikweichen einzubauen,

die die Lesbarkeit beeinträchtigen.

Promises

JMG / pixelio.de

Promises

Promises sind ein Mittel, um asynchronen Programmfluss zu steuern. Sie erhöhen die Lesbarkeit des Quellcodes und

helfen bei der Fehlerbehandlung.

PromisesListCtrl.$inject = ['$q', '$timeout'];function ListCtrl($q, $timeout) { function async() { return $q(function (resolve, reject) { $timeout(function () { resolve('Hello'); }, 1000); }); } async().then(function (data) { vm.data = data; });}

Testing?

Rike / pixelio.de

Tests

Mindestens zwei Ebenen von Tests: Unittests und End2End-Tests.

Manuelle Tests können allerdings nicht entfallen, da immer noch irgendwo Logikfehler versteckt sein können.

Unittests

Unittests

Karma: bietet die Infrastruktur. Steuerung der angeschlossenen Browser. Verteilung der Tests und

Einsammeln der Daten. Jasmine: Formulieren der Tests.

Die Tests liegen bei den Dateien, die sie testen sollen. Tests werden schneller gefunden und besser gepflegt, sind

gegenwärtiger.

describe('simple controller', function() { var $rootScope, $scope, createController; beforeEach(module('simpleCtrl')); beforeEach(inject(function($injector) { $rootScope = $injector.get('$rootScope'); $scope = $rootScope.$new(); var $controller = $injector.get('$controller'); createController = function() { return $controller('PasswordCtrl', { '$scope': $scope }); }; })); it('should have a name property with a value', function() { var controller = createController(); expect($scope.name).toBe('Hans-Peter'); }); it('should execute a function', function() { var controller = createController(); expect($scope.myFunc()).toBe('hello'); })});

End2End

End2End

Werden mit Protractor erstellt. Bauen auf Selenium WebDriver auf. Testen die komplette Applikation inklusive

Server. Längere Laufzeit als Unittests. Testen Integration einzelner

Komponenten in die Applikation.

Werden in einer parallelen Verzeichnishierarchie gepflegt.

End2End

describe('project list', function() { it('should check the first item in the list', function(done) { browser.get('/'); var name = element(by.repeater('project in projects') .row(0) .column('name')); expect(name.getText()).toEqual('Projekt A'); done(); });});

Test Doubles

Regina Kaute / pixelio.de

Test Doubles

Test Doubles für Services verwenden, z.B. $httpBackend.

Für alles Übrige kommt Sinon.js zum Einsatz.

Module Loader

Bernd Kasper / pixelio.de

Module Loader

Keine Script-Tags mehr schreiben. Abhängigkeiten zwischen Dateien werden aufgelöst. Können im Build zu einer Datei

zusammengefasst werden.

Beispiele: - require.js - Browserify - SystemJS - …

Module Loader

define(['angular'], function(angular) { var controller = angular.module('controller', []) .controller('ListCtrl', ListCtrl); function ListCtrl() {…} return controller; });

Statische Codeanalyse

Tim Reckmann / pixelio.de

Statische Codeanalyse

Statische Prüfung des Quellcodes. Auffinden von Antipatterns oder Betrachtung bestimmter Metriken wie z.B.

Komplexität.Tools:

- linter (jslint, jshint, eslint) - Plato

Statische Codeanalyse

Buildsysteme

schau media / pixelio.de

Buildsysteme

Automatisieren von wiederkehrenden Aufgaben wie Tests, Build, Linting

Beispiele: - grunt - gulp - npm

Buildsystememodule.exports = function(grunt) { grunt.initConfig({ copy: {...}, clean: { bower: ['bower_components'] }, shell: { bower: { command: 'bower install' } }, watch: { scripts: { files: ['src/**/*.js', 'src/**/*.html'], tasks: ['copy:app'] } }, requirejs: {...} }); require('load-grunt-tasks')(grunt); grunt.registerTask('bower', ['shell:bower', 'copy:bower', 'clean:bower']);};

Kommunikation

Aka / pixelio.de

Kommunikation

Wie werden Daten zwischen einzelnen Komponenten einer Applikation ausgetauscht?

Kommunikation innerhalb der Applikation

Services

Services sind Singletons und können an verschiedenen Stellen verwendet werden.

Mit Services lassen sich Nachrichtenbusse umsetzen, die zur Kommunikation zwischen den Komponenten verwendet

werden können.

angular.module('myApp', []) .factory('MsgBus', MsgBus); function MsgBus() { var callbacks = {}; return { on: on, trigger: trigger }; function on(event, callback) { if (callbacks[event]) { callbacks[event] = [callback]; return this; } callbacks[event].push(callback); } function trigger(event, data) { if (callbacks[event]) { callbacks[event].forEach(function (callback) { callback(data); }); } } }

Direktiven

Direktiven sind Marker im HTML, die den Funktionsumfang von HTML erweitern.

Mithilfe der Scope-Eigenschaft der Direktive kann zwischen dem außenliegenden Controller und der Direktive

kommuniziert werden.

Direktiven

<my-directive name="{{ name }}" color="color" reverse="reverseName()">

angular.module('app', []) .directive('myDirective', myDirective);function myDirective() { return { scope: { name: '@', // one way (read only) color: '=', // two way reverse: '&' // function binding } }}

Kommunikation zum Server

Kommunikation zum Server

Services, die die Kommunikation kapseln.

$http, restangular, ngResource, angular-websocket

Abstrahieren die Schnittstelle zum Server. Können zum Testen durch Stubs ersetzt werden.

Kommunikation zum Server

$http.get('/weather').success(function (data) { data.forEach(function (item) { if (item.id === id) { vm.data = item; } });});

Performance

Thomas Siepmann / pixelio.de

Performance

Angular ist ein Framework, das bedeutet Overhead.

Two Way Data-Binding mit Dirty Checking ist eines der größten Performance-Probleme.

Mögliche Lösung: angular-vs-repeat, ngGrid

Performance

Performance

Bei tiefergehenden Performance-Problemen: Developer Tools nutzen und die Applikation auseinander nehmen.

Fragen?

Rainer Sturm / pixelio.de

KONTAKT

Sebastian Springer [email protected]

Mayflower GmbH Mannhardtstr. 6 80538 München Deutschland

@basti_springer

https://github.com/sspringer82


Recommended