reactive javascript - froscon - 2016
TRANSCRIPT
REACTIVE JAVASCRIPT MIT RXJSREACTIVE JAVASCRIPT MIT RXJSMAXIMILIAN BERGHOFF - 21.08.2016 - FROSCONMAXIMILIAN BERGHOFF - 21.08.2016 - FROSCON
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
1 von 83 22.08.2016 08:58
STELL EUCH VOR, ...STELL EUCH VOR, ...
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
2 von 83 22.08.2016 08:58
ITERATOR PATTERNITERATOR PATTERN&&
OBSERVER PATTERNOBSERVER PATTERN
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
3 von 83 22.08.2016 08:58
RXJS - REACTIVE JAVASCRIPTRXJS - REACTIVE JAVASCRIPT
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
4 von 83 22.08.2016 08:58
DIE AKTEUREDIE AKTEURE
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
5 von 83 22.08.2016 08:58
Maximilian [email protected]/electrimaxxxMay�ower GmbH - WürzburgMaximilian.Berghoff@may�ower.de
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
6 von 83 22.08.2016 08:58
ITERATOR PATTERNITERATOR PATTERN
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
7 von 83 22.08.2016 08:58
var Iterator = function () {};
Iterator.prototype.next();
Iterator.prototype.rewind();
Iterator.prototype.current();
Iterator.prototype.hasNext();
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
8 von 83 22.08.2016 08:58
TRAVERSIERENTRAVERSIERENwhile (Iterator.hasNext()) { console.log(Iterator.next());}
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
9 von 83 22.08.2016 08:58
GEDANKENSPIELGEDANKENSPIELListe von CocktailsEigenschaften: id, name, zutaten, prozent, ...Aufgabe: "id & name von allem Contails mit prozent > 5.0"
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
10 von 83 22.08.2016 08:58
var cocktails = [ {id: 1001, name: 'Piña Colada', zutaten: [], prozent: 5.0 }, {id: 1002, name: ' Tequila Sunrise', zutaten: [], prozent: 6.0 }, {id: 1003, name: ' Long Island', zutaten: [], prozent: 7.0 },];
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
11 von 83 22.08.2016 08:58
var newList = [];for(var i = 0; i <= cocktails.length; i++) { if (cocktails[i].prozent > 5.0) { newList.push({id: cocktails[i].id, title: cocktails[i].title}) }}
console.log(newList);
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
12 von 83 22.08.2016 08:58
var newList = [];cocktails.forEach(function (cocktail) { if (cocktails[i].prozent > 5.0) { newList.push({id: cocktail.id, title: cocktail.title}) }});
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
13 von 83 22.08.2016 08:58
var godOnes = cocktails .filter(function (cocktail) { return cocktail.prozent > 5; }) .map(function (cocktail) { return {id: cocktail.id, name: cocktail.name}; });
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
14 von 83 22.08.2016 08:58
OBSERVER PATTERNOBSERVER PATTERN
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
15 von 83 22.08.2016 08:58
Observable.prototype.subscribe();
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
16 von 83 22.08.2016 08:58
Observer.prototype.notify();
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
17 von 83 22.08.2016 08:58
var Observable = function () {};
Observable.prototype.subscribe = function () {};
Observable.prototype.unsubscribe = function () {};
var Observer = function () {};
Observer.prototype.notify = function() {};
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
18 von 83 22.08.2016 08:58
HISTORYHISTORYReactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
19 von 83 22.08.2016 08:58
ERIK MEIERERIK MEIERBRAIN BACKMANBRAIN BACKMAN
MATHEW PODWYSOCKIMATHEW PODWYSOCKI
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
20 von 83 22.08.2016 08:58
LINQ TO EVENTSLINQ TO EVENTS
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
21 von 83 22.08.2016 08:58
VOLTAVOLTA
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
22 von 83 22.08.2016 08:58
WINDOWS FORMSWINDOWS FORMS<=><=>
WEB FORMSWEB FORMS
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
23 von 83 22.08.2016 08:58
BEISPIEL:BEISPIEL:
DRAG & DROPDRAG & DROPMAUSBEWEGUNG VERFOLGENMAUSBEWEGUNG VERFOLGEN
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
24 von 83 22.08.2016 08:58
EVENT LISTENER REGISTRIERENEVENT LISTENER REGISTRIERENelem.addEventListener('mousedown', mousedown, false);elem.addEventListener('mouseup', mouseup, false);elem.addEventListener('mousemove', mousemove, false);
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
25 von 83 22.08.2016 08:58
MOUSE DOWNMOUSE DOWNfunction mousedown(e) { isDown = true; state = { startX: e.offsetX, startY: e.offsetY};}
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
26 von 83 22.08.2016 08:58
MOUSE MOVEMOUSE MOVEfunction mousemove(e) { if (!isDown) {return;} var delta = { endX: e.clientX - state.startX, endY: e.clientY - state.startY };}
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
27 von 83 22.08.2016 08:58
MOUSE UPMOUSE UPfunction mouseup (e) { isDown = false; state = null;}
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
28 von 83 22.08.2016 08:58
UNSUBSCRIBEUNSUBSCRIBEfunction dispose() { elem.removeEventListener('mousedown', mousedown, false); elem.removeEventListener('mouseup', mouseup, false); elem.removeEventListener('mousemove', mousemove, false);}
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
29 von 83 22.08.2016 08:58
DIE HOCHZEITDIE HOCHZEIT
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
30 von 83 22.08.2016 08:58
REACTIVE EXTENSIONREACTIVE EXTENSIONRxJavaRxJSRx.NetRx.ScalaRx.ClojureRx.Swift...
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
31 von 83 22.08.2016 08:58
REACTIVEX.IOREACTIVEX.IO
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
32 von 83 22.08.2016 08:58
GITHUB.COM/REACTIVE-EXTIONSIONGITHUB.COM/REACTIVE-EXTIONSION
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
33 von 83 22.08.2016 08:58
STREAM VON EVENTSSTREAM VON EVENTS
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
34 von 83 22.08.2016 08:58
var list = [1, 2, 3, 4, 5];
list.forEach(function (item) { console.log("nexItem: %s", item);});
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
35 von 83 22.08.2016 08:58
var list = [1, 2, 3, 4, 5];
var source = Rx.Observable.fromArray(list);
var disposal = source.subscribe( function (x) {console.log('Next: ' + x);}, function (err) {console.log('Error: ' + err);}, function () {console.log('Completed');});
disposal.dispose();
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
36 von 83 22.08.2016 08:58
ASCII MURMELNASCII MURMELN...1...2...3...4...5.> Rx.Observable.fromArray([1, 2, 3, 4, 5])...1...2...3...4...5| 1, 2, 3, 4, 5 are emitted valuesX is an error| is the 'completed' signal...> is the timeline
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
37 von 83 22.08.2016 08:58
OBSERVEROBSERVER
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
38 von 83 22.08.2016 08:58
var disposal = source.subscribe( function (x) {console.log('Next: ' + x);}, function (err) {console.log('Error: ' + err);}, function () {console.log('Completed');});
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
39 von 83 22.08.2016 08:58
function Observer() { }
Observer.prototype.onNext = function (value) { ... };
Observer.prototype.onError = function (error) { ... };
Observer.prototype.onCompleted = function () { ... };
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
40 von 83 22.08.2016 08:58
var source = Rx.Observable.range(1,10);
var reducedSource = source.filter(function (value) {
return value % 2 === 0;});
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
41 von 83 22.08.2016 08:58
...1...2...3...4...5...6...7...8...9...10> Rx.Observable.range(1, 10)
.......2.......4.......6.......8.......10| reducedSource
...1...2...3...4...5...6...7...8...9...10| source
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
42 von 83 22.08.2016 08:58
OBSERVALBEOBSERVALBE
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
43 von 83 22.08.2016 08:58
function Disposable() { }
Disposable.prototype.dispose = function () { ... }
function Observable() { }
/** * @return Disposable */Observable.prototype.subscribe = function (observer) { ... }
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
44 von 83 22.08.2016 08:58
(ER-) ZEUGUNG(ER-) ZEUGUNG
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
45 von 83 22.08.2016 08:58
Rx.Observable.create();
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
46 von 83 22.08.2016 08:58
var source = Rx.Observable.create(function (observer) { observer.onNext(42); observer.onCompleted(); return function () { console.log('disposed'); }});
var subscription = source.subscribe( function (x) { console.log('onNext: %s', x); }, function (e) { console.log('onError: %s', e); }, function () { console.log('onCompleted'); } );
subscription.dispose();
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
47 von 83 22.08.2016 08:58
> onNext: 42> onCompleted> disposed
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
48 von 83 22.08.2016 08:58
Rx.Observable.range();
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
49 von 83 22.08.2016 08:58
Rx.Observable.fromEvent(element, eventName, [selector])// oderRx.Observable.fromCallback(func, [context], [selector])
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
50 von 83 22.08.2016 08:58
var input = $('#input');
var source = Rx.Observable.fromEvent(input, 'keyup');
var subscription = source.subscribe( function (x) {console.log('Next: key pressed!');}, function (err) {console.log('Error: %s', err);}, function () {console.log('Completed');});
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
51 von 83 22.08.2016 08:58
var fs = require('fs'), Rx = require('rx');
var exists = Rx.Observable.fromCallback(fs.exists);var source = exists('file.txt');
var subscription = source.subscribe( function (x) {console.log('Next: ' + x);}, function (err) {console.log('Error: ' + err);}, function () {console.log('Completed');} );
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
52 von 83 22.08.2016 08:58
ARBEITENARBEITEN
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
53 von 83 22.08.2016 08:58
LINQLINQLANGUAGE INTEGRATED QUERYLANGUAGE INTEGRATED QUERY
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
54 von 83 22.08.2016 08:58
KOMBINATIONKOMBINATION.concat();// oder.merge();
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
55 von 83 22.08.2016 08:58
CONCAT()CONCAT()...1...2...3...4...5..> sourceOne = Rx.Observable.range(1, 5)..6..7..8..9..10.> sourceTwo = Rx.Observable.range(6, 5)...1...2...3...4...5..6..7..8..9..10| sourceOne.concat(sourceTwo)
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
56 von 83 22.08.2016 08:58
MERGE()MERGE()...1...2...3...4...5..> sourceOne = Rx.Observable.range(1, 5)..6..7..8..9..10.> sourceTwo = Rx.Observable.range(6, 5)...?...?...?...?...?...?...?...?...?...?| sourceOne.merge(sourceTwo)
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
57 von 83 22.08.2016 08:58
MERGE()MERGE()...1...2...3...4...5..> sourceOne = Rx.Observable.range(1, 5)..6..7..8...9..10.> sourceTwo = Rx.Observable.range(6, 5)..61.7.28..39.410..5.| sourceOne.merge(sourceTwo)
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
58 von 83 22.08.2016 08:58
FILTERFILTER
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
59 von 83 22.08.2016 08:58
var source = Rx.Observable.range(1,10);
var filtered = source.filter(function (x) { return x % 2 === 0;});
...1...2...3...4...5...6...7...8...9...10> Rx.Observable.range(1, 10)
......2......4.........6.......8.......10| filtered
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
60 von 83 22.08.2016 08:58
PROJEKTIONENPROJEKTIONEN
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
61 von 83 22.08.2016 08:58
var list = [ {id: 1001, name: 'Piña Colada', zutaten: [], prozent: 5.0 }, {id: 1002, name: ' Tequila Sunrise', zutaten: [], prozent: 6.0 }, {id: 1003, name: ' Long Island', zutaten: [], prozent: 7.0 },];
var source = Rx.Observable.from(list);var ids = source.map(function (item) { return item.id;});var disposal = ids.subscribe(function (x) { console.log('onNext Id: ' + x);});
disposal.dispose();
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
62 von 83 22.08.2016 08:58
OUTPUTOUTPUTonNext Id: 1001onNext Id: 1002onNext Id: 1003
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
63 von 83 22.08.2016 08:58
??.flatMap();
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
64 von 83 22.08.2016 08:58
var source = Rx.Observable .range(1, 2) .flatMap(function (x) { return Rx.Observable.range(x, 2); });
var subscription = source.subscribe( function (x) { console.log('onNext: ' + x); });
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
65 von 83 22.08.2016 08:58
return Rx.Observable.range(1, 2);return Rx.Observable.range(2, 2);
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
66 von 83 22.08.2016 08:58
OUTPUTOUTPUT> onNext: 1> onNext: 2> onNext: 2> onNext: 3
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
67 von 83 22.08.2016 08:58
NOCH MEHR?NOCH MEHR?GITHUB/DOKUMENTATIONGITHUB/DOKUMENTATION
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
68 von 83 22.08.2016 08:58
PROMISES?PROMISES?Single ValueCancellation?
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
69 von 83 22.08.2016 08:58
ARRAY OPERATORENARRAY OPERATORENVS.VS.
RX OPERATORENRX OPERATOREN
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
70 von 83 22.08.2016 08:58
ARRAY OPERATORENARRAY OPERATORENKomplette Liste wird durch gereichtdabei auf jedem Eintrag
Projektion - map, ..Ge�ltert - reduce, �lterErgänzt - concat, merge
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
71 von 83 22.08.2016 08:58
RX OPERTORENRX OPERTORENJedes Event/Jeder Eintrag einzelndabei
Projektion - map, ..Ge�ltert - reduce, �lterErgänzt - concat, merge
Filter = STOP => nicht weiter gereichtErgänzung nur der Zugang für weiteren Stream
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
72 von 83 22.08.2016 08:58
ACTIONACTION
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
73 von 83 22.08.2016 08:58
<input type="text" id="input"/>
<h2>Results</h2><ul id="results"></ul>
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
74 von 83 22.08.2016 08:58
var $input = $('#input'); var $results = $('#results');
var suggestions = Rx.Observable.fromEvent($input, 'keyup');
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
75 von 83 22.08.2016 08:58
var suggestions = Rx.Observable.fromEvent($input, 'keyup') .pluck('target', 'value') .filter(function(text) { return text.length > 2 }) .debounce(500 /* ms */) .distinctUntilChanged();
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
76 von 83 22.08.2016 08:58
... flatMapLatest(function (term) { return $.ajax({ url: 'https://en.wikipedia.org/w/api.php', dataType: 'jsonp', data: { action: 'opensearch', format: 'json', search: term } }).promise(); });
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
77 von 83 22.08.2016 08:58
... .subscribe( function(data) { $results .empty() .append($.map(data[1], function (value) { return $('<li>').text(value); })) }, function(error) { $results .empty() .append($('<li>')) .text('Error:' + error); } );
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
78 von 83 22.08.2016 08:58
JS FIDDLEJS FIDDLE
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
79 von 83 22.08.2016 08:58
QUESTIONS?QUESTIONS?Ask Now!Twitter: @ElectricMaxxxMail: Maximilian.Berghoff@may�ower.de
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
80 von 83 22.08.2016 08:58
LINKSLINKSRxJS docsMarblesListe an TutorialsRepositoryAusführliches TutorialVideo TutorialsBuch
ALTERNATIVENALTERNATIVEN
cyclejsBACONJS
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
81 von 83 22.08.2016 08:58
THANK YOU!THANK YOU!
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
82 von 83 22.08.2016 08:58
< <
Reactive JavaScript mit RxJS - FrOSCon - 2016 http://localhost:8000/Reactive/?print-pdf/#/
83 von 83 22.08.2016 08:58