-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
750 lines (641 loc) · 23.4 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>bk-intro-to-ng2</title>
<meta name="description" content="An intro to ng2">
<meta name="author" content="Ben Kinsy">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui">
<link rel="stylesheet" href="css/reveal.css">
<link rel="stylesheet" href="css/theme/black.css" id="theme">
<!-- Code syntax highlighting -->
<link rel="stylesheet" href="lib/css/zenburn.css">
<!-- Printing and PDF exports -->
<script>
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
</script>
<!--[if lt IE 9]>
<script src="lib/js/html5shiv.js"></script>
<![endif]-->
<style>
th, td {
text-align: center !important;
}
.vs-column {
color: gray;
}
</style>
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<h1>intro to ng2</h1>
<h3 style="text-transform:none">
Angular2 aka <a href="angular.io">angular.io</a> aka ng2 aka Angular
</h3>
<p>(as opposed to AngularJS which refers to Angular1)</p>
<p>Ben Kinsey <a href="http://twitter.com/bkinsey808">@bkinsey808</a></p>
<p>Github Demo Project: <a href="https://github.com/bkinsey808/bk-ng2-ultra">github.com/bkinsey808/bk-ng2-ultra</a></p>
</p>
</section>
<section>
<h2>What is Angular2?</h2>
<div class="fragment">
<div class="fragment strike">A framework for building mobile and desktop applications</div>
<div class="fragment">A compiler!</div>
<div class="fragment">(sort of)</div>
</div>
</section>
<section>
<table>
<tr>
<th style="font-size:2em">ng2</th>
<th class="vs-column">vs</th>
<th style="font-size:2em">react</th>
</tr>
<tr class="fragment">
<th>typescript</th>
<th class="vs-column">written in</th>
<th>es5</th>
</tr>
<tr class="fragment">
<th>beta.2</th>
<th class="vs-column">version</th>
<th>0.15</th>
</tr>
<tr class="fragment">
<th>google</th>
<th class="vs-column">megacorp</th>
<th>facebook</th>
</tr>
<tr class="fragment">
<th>framework</th>
<th class="vs-column">considered as</th>
<th>library</th>
</tr>
<tr class="fragment">
<th>more opinionated</th>
<th class="vs-column"></th>
<th>less opinionated</th>
</tr>
<tr class="fragment">
<th>more corporate</th>
<th class="vs-column"></th>
<th>more hipster</th>
</tr>
</table>
</section>
<section>
<table>
<tr>
<th style="font-size:2em">ng2</th>
<th class="vs-column">vs</th>
<th style="font-size:2em">react</th>
</tr>
<tr class="fragment">
<th>dirty checking</th>
<th class="vs-column">change detection</th>
<th>virtual dom diffing</th>
</tr>
<tr class="fragment">
<th>more complex</th>
<th class="vs-column"></th>
<th>simpler</th>
</tr>
<tr class="fragment">
<th>more extensible</th>
<th class="vs-column">change detection strategy</th>
<th>less extensible</th>
</tr>
</table>
</section>
<section>
<p style="font-size:2em">ng2 change detection</p>
<p class="fragment" style="font-size:2em; text-decoration: underline;">super optimized</p>
<div class="fragment">
<p>different strategies for different scenarios</p>
<p>jit change detection</p>
<p>inline caching</p>
<p>only updates changed bindings</p>
<p>can be further optimized with immutables and observables</p>
</div>
</section>
<section>
<p style="font-size:2em">ng2 uses Zone.js</p>
<p>tells ng2 when to run change detection</p>
<p>How it works:</p>
<div class="fragment">
<p class="fragment strike">intercepts all asynchronous events so they map to the same thread local context</p>
</div>
<p class="fragment">MAGIC!</p>
<ul class="fragment">
<li><strong>no more digest loop</strong></li>
<li>no more $timeout</li>
<li>no more $scope.$apply(), ever</li>
</ul>
</section>
<section>
<img src="img/ng1-graveyard.png" style="width:500px;">
</section>
<section>
<img src="img/ng2-perf-1.png" style="width:800px;">
</section>
<section>
<img src="img/ng2-perf-2.png" style="width:800px;">
</section>
<!--
<section>
<img src="img/ng2-perf.png" style="width:900px;">
</section>
-->
<section>
<img src="img/migrate-from-ng1-to-ng2.png" style="width:900px;">
</section>
<section>
<p style="font-size:2em">ng2 is engineered to be Opinionated</p>
<p>Even more opinionated than ng1, and ng1 was considered a relatively opinionated framework</p>
<p>Why is Opionated a Good Thing?</p>
<div class="fragment">
<p>Angular community includes 1.1 million active developers</p>
<p>Massive scale community consensus and synergy</p>
</div>
</section>
<section>
<p style="font-size:2em">Learn TypeScript!</p>
<p class="fragment" style="font-size:1em;">(Don't worry, it's awesome)</p>
<div class="fragment">It's just...</div>
<div style="font-size:1.5em" class="fragment">ES6 + ES* + optional static typing</div>
<div class="fragment">That's it!!!</div>
</section>
<section>
<div style="float: left; width:50%">
AngularJS
<pre><code data-noescape>import angular, {bootstrap}
from 'angular';
angular.module('app', [])
.component('app', {
restrict: 'E',
template: `
<div>
Hello World
</div>
`,
controller: class App {}
});
bootstrap(document, ['app']);</code></pre>
</div>
<div class="fragment" style="float: right; width:50%">
Angular2
<pre><code data-noescape>import {bootstrap} from
'angular2/platform/browser';
import {Component} from
'angular2/core';
@Component({
selector: 'app',
template: `
<div>
Hello World
</div>
`
})
class App {}
bootstrap('App', []);</code></pre>
<small>Slide credit: Patrick Stapleton</small>
</div>
</section>
<section>
<p style="font-size:1.5em; text-decoration:underline">What the @ are decorators?</p>
<p>simply functions that modify:</p>
<p>• methods</p>
<p>• method parameters</p>
<p>• properties</p>
<p>• classes</p>
<p>Decorators are typically used to add metadata,
but could also add mixin-like functionalty</p>
</section>
<section>
<p>@<u>Component</u> - tells ng2 that the class defines a component, and also how to process it</p>
<ul style="padding-left:50px">
<li><u>selector</u> - a css selector (DOM element tag) that tells ng2 to create and insert an instance of this component in the parent HTML</li>
<li style="padding-top:30px"><u>template</u> - the template string</li>
<!--
<li><u>directives</u> - an array of the Components or Directives that this template requires</li>
<li><u>providers</u> - an array of dependency injection providers for services that the component requires</li>
-->
</ul>
</section>
<section>
<p style="font-size:1.5em">An ng2 app is a tree of components</p>
<p class="fragment">A component is self-describing:</p>
<ul class="fragment">
<li>knows how to interact with its host element.</li>
<li>knows how to render itself.
<li>configures dependency injection
<li>has a well-defined public API of input and output properties.
</ul>
<p class="fragment">Scope is not inherited down the DOM tree. <u>There is no scope.</u></p>
</section>
<section>
<p style="font-size:1.5em">What's up with Dependency Injection?</p>
<p>"Don't call me, I'll call you"</p>
<p>The technical name for this is "Inversion of Control"</p>
<p>ng2, like ng1, controls how dependencies get injected into your custom code</p>
<p>This is a design pattern that increases modularity and extensibility<p>
</section>
<section>
<p style="font-size:1.5em"><u>The evolution of Dependency Injection</u></p>
<div class="fragment" style="float:left; width:50%">
ng1 uses quoted strings:
<pre><code data-noescape> ['a', 'b', function(a, b) {..}]</code></pre>
This is necessary, because under the hood, a simple object in ng1 is used to store the
name-value (token-object) pairs of each dependency:
<pre><code data-noescape> di = {'a': a, 'b': b}</code></pre>
</div>
<div class="fragment" style="float:right; width:50%">
In ng2, dependency injection is implemented as an es6 Map(),
which can take objects, functions, etc as keys, not just simple strings.
<pre><code data-noescape> di = new Map()</code></pre>
The end result is that in ng2 dependencies can be modeled as a simple array:
<pre><code data-noescape> providers: [a, b]</code></pre>
</div>
</section>
<section>
<p style="font-size:1.5em">What is a <u>Provider</u>?</p>
<p class="fragment">an instruction that describes how an
object for a certain token is created</p>
<div class="fragment">
<p>In ng2 there are two ways to inject an object (typically a service)
so that it can be used by a component:</p>
<pre><code data-noescape>// at bootstrap
bootstrap(AppComponent, [DataService]);
// in a component
@Component({
...
providers: [DataService]
})
class AppComponent {
constructor(dataService: DataService) {
// dataService instanceof DataService === true
}
}</code></pre>
</div>
</section>
<section>
<p style="font-size:1.5em"><u>Multi Providers</u>: ng2 pluggable hooks</p>
<p class="fragment">can provide multiple tokens</p>
<div class="fragment">
<p>can also be used to extend...</p>
<pre><code data-noescape>class Engine { }
class TurboEngine { }
var injector = Injector.resolveAndCreate([
provide(Engine, {useClass: Engine}),
provide(Engine, {useClass: TurboEngine})
]);
var engine = injector.get(Engine);
// engine instanceof TurboEngine</code></pre>
</div>
</section>
<section>
<p>ng2 encourages "composition over inheritance"</p>
<p class="fragment" style="font-size:2em">Last token WINS</p>
<p class="fragment">...a very nice feature to implement a pluggable interface that can be extended from the outside world</p>
<p class="fragment">...including ng2 platform directives</p>
</section>
<section>
<p style="font-size:1.3em">Wait... did you say <u>Directives?</u></p>
<p class="fragment">Directives are classes which get instantiated as a response to a particular DOM structure</p>
<p class="fragment">By controlling the DOM structure, what directives are imported, and their selectors, the developer can use the "composition pattern"</p>
<p class="fragment">using simple objects to build complex ones.</p>
<p class="fragment">Directives are the cornerstone of an Angular application.</p>
<p class="fragment">Directives allow the developer to turn HTML into a DSL and then control the application assembly process.</p>
</section>
<section>
<p style="font-size:1.3em"><u>ng2 directives</u></p>
<p>Directives are instantiated whenever the CSS selector matches the DOM structure.</p>
<p>@Component is a special kind of @Directive that matches the tag selector</p>
<p>@Directive is used when matching any other css selector (e.g. custom tooltip attribute)</p>
</section>
<section>
<p style="font-size:1.5em">Let's talk Templates</p>
<p>ng2 templates are compiled at compile time instead of runtime for optimization</p>
<p><strong>Key Point:</strong> same syntax for custom and native components</p>
<p>friendly for tooling (though not much ng2 template tooling has been built yet)</p>
</section>
<section>
<p style="font-size:1.5em">Explicit Template Syntax</p>
<p>makes template easier to refactor without understanding underlying components</p>
<p>for example, *ngIf and *ngFor cannot be combined in same element, therefore there is no need for
the concept of "directive priority"</p>
</section>
<section>
<p style="text-decoration: underline; font-size:1.2em">What the #*{{|}}*[()] did you do to my HTML?</p>
<small class="fragment">calm down, it actually makes a lot of sense</small>
<div class="fragment">
<p>[] property binding</p>
<p>() event binding</p>
<p>[()] two way binding</p>
<p>{{}} interpolation</p>
<p>* template tag (ngIf, ngFor, etc)</p>
<p># local variable</p>
<p>| pipe</p>
</div>
</section>
<section>
<p style="font-size:1.5em">The evolution of forms</p>
<p>ng1 forms rely on the <strong>ng-model</strong> directive</p>
<p>instantaneous two-way data binding keeps a form control in sync with a view model.</p>
<div class="fragment">
<p>However, the approach has some disadvantages:</p>
<ul>
<li>everything is mutable</li>
<li>strange, unintended side-effects if same model is used elsewhere</li>
<li>validation logic often contaminates templates</li>
<li>form testing must be end-to-end because forms are implemented directly as DOM.</li>
</ul>
</div>
</section>
<section>
<div style="float: left; width: 50%">
<u>Template-driven forms</u>
<ul style="font-size:.8em">
<li>quick and easy, similar to ng1 forms</li>
<li>simpler, easier, and similar to ng1 forms</li>
<li>Can be implemented with very little component code</li>
<li>Hard to unit test</li>
<li>allows validation logic to infest the template (bad!)</li>
<li>does not work with immutable objects</li>
<li>everything is mutable, almost diametric opposite of redux pattern</li>
</ul>
</div>
<div style="float: right; width: 50%">
<u>Model-based forms</u>
<ul style="font-size:.8em">
<li>best practice for advanced use cases</li>
<li>more complex and very different from ng1 forms</li>
<li>requires non-trivial component code</li>
<li>validation logic must be in the component code (good!)</li>
<li>works with immutable objects</li>
<li>state is now easier to centralize and manage, making redux pattern possible</li>
<li class="fragment"><u>form and controls are observables leading to modern FRP (functional reactive programming) patterns</u></li>
</ul>
</div>
</section>
<section>
<p style="font-size:1.3em">What's an Observable and why is it a big deal?</p>
<p style="margin-top:60px">Function types in es7/es2016
<table>
<tr>
<th></th>
<th>Synchronous</th>
<th>Asynchronous</th>
</tr>
<tr>
<th>function</th>
<td>T</td>
<td>Promise</td>
</tr>
<tr>
<th>function*</th>
<td>Iterator</td>
<td><span class="fragment">Observable?</span></td>
</tr>
<tr>
</tr>
</table>
</section>
<section>
<img src="img/everything-is-a-stream.jpeg" style="width:700px;">
</section>
<section>
<img src="img/compose-all-the-events.jpeg" style="width:700px;">
</section>
<section>
<p style="font-size:1.5em">Ready for the Rx Revolution?</p>
<p>in ng1 promises were prevalent, in ng2 it will be observables.</p>
<div class="fragment">
<p>observables have advantage over promises:</p>
<ul>
<li>disposability (can be canceled before completed)</li>
<li>lazy (will not get evaluated unless subscribed)</li>
<li>re-usable (can be subscribed to and unsubscribed to multiple times)</li>
<li>returns a stream of values, like an array but async</li>
<li>can handle backpressure</li>
<li>much better suited for real world client-server communication than promises</li>
</ul>
</div>
</section>
<section>
<p>Template-driven Form Example Template</p>
<pre><code data-noescape><form #f="form"
(ng-submit)="onSubmitTemplateBased()">
<label>First Name:</label>
<input type="text"
ng-control="firstName"
[(ng-model)]="vm.firstName" required>
<label>Password:</label>
<input type="password"
ng-control="password"
[(ng-model)]="vm.password" required>
<button type="submit"
[disabled]="!f.valid">
Submit
</button>
</form></code></pre>
</section>
<section>
<p>Model-based Form Example Template</p>
<pre><code data-noescape><form [ng-form-model]="form"
(ng-submit)="onSubmitModelBased()">
<label>First Name:</label>
<input type="text"
ng-control="firstName">
<label>Password:</label>
<input type="password"
ng-control="password">
<button type="submit"
[disabled]="!form.valid">
Submit
</button>
</form></code></pre>
</section>
<section>
<p>Template-driven Form Example Component</p>
<pre><code data-noescape>@Component({
selector: "template-driven-form",
templateUrl: 'template-driven-form.html',
directives: [FORM_DIRECTIVES]
})
export class TemplateDrivenFormComponent {
vm: Object = {};
onSubmitTemplateBased() {
console.log(this.vm);
}
}</code></pre>
</section>
<section>
<p>Template-driven Form Example Component</p>
<div style="font-size:.8em">
<pre><code data-noescape>@Component({
selector: "model-driven-form",
templateUrl: 'model-driven-form.html',
directives: [FORM_DIRECTIVES]
})
export class ModelDrivenFormComponent {
form: ControlGroup;
firstName: Control = new Control("", Validators.required);
constructor(fb: FormBuilder) {
this.form = fb.group({
"firstName": this.firstName,
"password": ["", Validators.required]
});
}
onSubmitModelBased() {
console.log(this.form);
}
}</code></pre></div>
</section>
<section>
<p>Now comes the fun part.</p>
<p>This is FRP (Functional Reactive Programming)</p>
<pre><code data-noescape>this.form.valueChanges
.map((value) => {
value.firstName = value.firstName.toUpperCase();
return value;
})
.filter((value) => this.form.valid)
.subscribe((value) => {
alert("View Model = " + JSON.stringify(value));
});</code></pre>
</section>
<section>
ng2's http.get() returns an observable
<div style="float:left; width:50%">
<pre><code data-noescape>getTasks() {
return this.http
.get('/api/v1/tasks.json')
.map( (responseData) => {
return responseData.json();
})
.map((tasks: Array<any>) => {
let result:Array<Task> = [];
if (tasks) {
tasks.forEach((task) => {
result.push(</code></pre></div>
<div style="float:right; width:50%">
<pre><code data-noescape> new Task(
task.id,
task.description,
task.dueDate,
task.complete));
});
}
return result;
});
}
}</code></pre></div>
</section>
<section>
<p>This is a service that wraps an observable</p>
<div style="font-size:0.9em">
<pre><code data-noescape>@Injectable()
export class WikipediaService {
constructor(
private jsonp: Jsonp) {}
search (term: string) {
var search = new URLSearchParams()
search.set('action', 'opensearch');
search.set('search', term);
search.set('format', 'json');
return this.jsonp
.get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK', {
search
})
.map((response) =>
response.json()[1]);
}
}</code></pre></div>
</section>
<section>
<p>Search-as-you-type powered by observables</p>
<div style="float:left; width:50%; font-size:0.8em">
<pre><code data-noescape>@Component({
selector: 'wikipedia-search',
providers: [WikipediaService],
template: `
<div>
<h2>Wikipedia Search</h2>
<input
type="text"
[ngFormControl]="term">
<ul>
<li *ngFor="#item of items
| async">
</li>
</ul>
</div>
`
})</code></pre></div><div style="float:right; width:50%; font-size:0.8em"><pre><code data-noescape>export class WikipediaSearchComponent {
items: Observable<Array<string>>;
term = new Control();
constructor(
private wikipediaService:
WikipediaService) {
this.items =
this.term.valueChanges
.debounceTime(400)
.distinctUntilChanged()
.switchMap(term =>
this.wikipediaService
.search(term));
}
}</code></pre></div>
</section>
<section>
<p style="font-size:1.3em">A taste of learning about Rx instance operators</p>
<p>With flatMap, the search results could be stale, because search responses may come back out of order. To fix this, switchMap should be used, since it ensures that an old observable is unsubscribed once a newer one is provided.</p>
<p>So, in summary, flatMap should be used when all results matter, regardless of their timing, and switchMap should be used when only results from the last Observable matter.</p>
</section>
<section>
<p style="font-size:1.3em">The stateful <u>AsyncPipe</u></p>
<p>The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically, eventually returning the emitted value(s).</p>
<p>It is stateful because the pipe maintains a subscription to the input and its returned values depend on that subscription.</p>
</section>
<section>
<p style="font-size:1.3em">Bottom Line:</p>
<p>ng2 plus Rx pushes the state-of-the-art dramatically.</p>
<p>It's never been easier to write powerful, responsive UI.</p>
<p>Hopefully, this presentation has given you a little glimpse into emerging patterns that I predict will become well-used in this field in the future.</p>
</section>
</div>
</div>
<script src="lib/js/head.min.js"></script>
<script src="js/reveal.js"></script>
<script>
// Full list of configuration options available at:
// https://github.com/hakimel/reveal.js#configuration
Reveal.initialize({
controls: true,
progress: true,
history: true,
center: true,
transition: 'slide', // none/fade/slide/convex/concave/zoom
// Optional reveal.js plugins
dependencies: [
{ src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } },
{ src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
{ src: 'plugin/zoom-js/zoom.js', async: true },
{ src: 'plugin/notes/notes.js', async: true }
]
});
</script>
</body>
</html>