The whole workshop is open source: https://github.com/georgiee/angular-workshop-skipbo
---
Angular Advanced Workshop we gave for KaiserX (Allianz, Dec 2018) to teach advanced topics of Angular in a playful way.
The workshop covered six chapters:
Modules, Components, Routing, RxJS, Testing & Animations.
Each Chapter included a challenge, a markdown document describing step by step what to do while giving challenging tasks to solve. The challenges include git branches to catch up if someone gets lost.
We even covered advanced topics like the event loop (micro/macro tasks), zone.js, onPush, ChangeDetection & switchMap vs mergeMap. I'm so happy that I could share this knowledge and people understood it.
By following my workshop, all 15 developers created a fully working Skip-Bo game within three days. There are animations, advanced unit tests, a implemented AI called Oscar to play for the CPU opponents and so much more!
And the best part
We made everything open source. The whole workshop can be found on github: https://github.com/georgiee/angular-workshop-skipbo
Here the final result:
https://skipbo-angular-workshop.netlify.com
26. Your tasks
· Create our GameService
· Provide expected interface (TDD)
· Inject the GameService
· Break the Injection and fix it
· Answer a quick question
35. View Encapsulation
· BEM anyone?
· Scoped Styles in ShadowDOM standard
!
· Angular Native & Emulated
· Native 0, ShadowDom 1
· (Example)
36. Smart & Dumb
· presentational not dumb!
· presentational: rely on inputs only, no
huge logic inside
· smart: state, sometimes business logic,
fetch data
· (Example)
37. OnPush
· Important Concept but CD makes it
difficult
· Rely only on inputs (presentational )
· Performance: Website Example
· Still updating: UI events, Async Pipes
· Live Example
46. Router Outlet
· Anchor point for mounted components
· Exposes reference #myTemplateVar="outlet"
47. Lazy Load
So easy to do.
loadChildren: './lazy-load/lazy-load.module#LazyLoadModule'
· Works by convention
· Usually you use a empty route inside.
· (Example)
48. Manual Loading a Module
· lazyModules key in angular.json
· creates a chunk
· load by convention url
· Use it with NgModuleFactoryLoader
· (Example)
49. Guards
· Protect your pages
· Interfaces: CanActivate, CanDeactivate, ..
· ng g guard my-protector
· (Example)
50. Resolver
· Ensure a component gets its data
· Access via route.snapshot.data
· Example
64. · Introduction
· Debugging
· About Dollar Signs
· Cold vs Hot Observables
· Make Cold Observables Hot
· RxJS in the wild
· Testing
65. Introduction
· Extended Observer Pattern (Gang of Four)
· Subject and Observers
· Event System is an Observer Pattern
Chapter 04 — RxJS: Introduction
70. Tag =
!
Key; notification = subscribe<br>
Tag =
!
Key; notification = next; value = {key: "a"…}
Tag =
!
Key; notification = next; value = {key: "b"…}
!
Key notification = unsubscribe<br>
Chapter 04 — RxJS: Debugging
71. Dollar Sign
const click$ = Observable.fromEvent(button, 'click');
· pluralization, called Finnish notation
· peopl€, mic€, oxe₦
72. Cold vs Hot Observables
A cold observable creates its producer on
each subscription, a hot observables closes
over an already existing instance.
— Ben Lesh
Chapter 04 — RxJS: Cold vs Hot
73. // COLD (unicast)
var cold = new Observable((observer) => {
var producer = new Producer();
producer.listen(() => {
observer.next()
});
});
// HOT (multicast)
var producer = new Producer();
var hot = new Observable((observer) => {
producer.listen(() => {
observer.next()
});
});
Chapter 04 — RxJS: Cold vs Hot
74. Make Cold Observable Hot
· Cold: Create a producer (like a
websocket) for each subscriber
· Make Hot: Create only one producer,
then send same data to all susbcribers
Chapter 04 — RxJS: Make Cold Observable Hot
75. const myInterval = interval(500).pipe(
tap(value => console.log('interval produced a value'))
);
myInterval.subscribe(value => {
console.log('received a value', value)
});
myInterval.subscribe(value => {
console.log('received a value', value)
});
/**
interval produced a value
received a value 0
interval produced a value
received a value 0
*/
Chapter 04 — RxJS: Make Cold Observable Hot
76. const myInterval = interval(500).pipe(
tap(value => console.log('interval produced a value'))
);
const subject = new Subject();
// 1. let this subject subscribe to the cold observable
myInterval.subscribe(subject);
// 2. now let future observables subscribe to the subject instead of the interval
subject.subscribe(value => console.log('received a value', value));
subject.subscribe(value => console.log('received a value', value));
subject.subscribe(value => console.log('received a value', value));
/**
interval produced a value
received a value 0
received a value 0
received a value 0
*/
Chapter 04 — RxJS: Make Cold Observable Hot
77. RxJS in the wild
· asObservable vs. Subject
· BehaviourSubject
· destroy & takeUntil
· toArray
78. private _changed: Subject<any> = new Subject();
get changed(): Observable<any> {
return this._changed.asObservable();
}
· A subject is both an observer and
observable
· Prevent the observer part (next)
· changed.next('new value')
Chapter 04 — RxJS: RxJS in the wild
79. · Hot Observables can produce values
without someone listening.
· Page mounted vs Data already
delivered
!
· BehaviorSubject is the solution
Chapter 04 — RxJS: RxJS in the wild
80. const subjectA = new Subject();
const subjectB = new BehaviorSubject(null);
subjectA.next('your loaded data');
subjectB.next('your loaded data');
subjectA.subscribe(value => console.log('value from subjectA:', value));
subjectB.subscribe(value => console.log('value from subjectB:', value));
// value from subjectB: your loaded data
Chapter 04 — RxJS: RxJS in the wild
81. · addEvenListener -> removeEventListener
· subscribe -> unsubscribe
· This is bad
class YourComponent {
initService() {
this.yourService.subscribe(data => {
// do something nice
})
}
}
Chapter 04 — RxJS: RxJS in the wild
83. private _destroyed: Subject<any> = new Subject();
initService() {
this.yourService
.pipe(
takeUntil(this._destroyed)
).subscribe(data => {
// do something nice
})
}
ngOnDestroy() {
this._destroyed.next();
}
Chapter 04 — RxJS: RxJS in the wild
84. RxJS Testing
· RxJS is basically synchronous
· Test for effects, don't test the stream
itself.
· Forward time with tick & fakeAsync
· Never use Marble Testing to test streams
· (Example rxjs/testing)
97. What are Macro Tasks?
Queued up but allow the browser engine
to render between each task.
· scripts
· setTimeout / setInterval
· event listener callbacks
document.appendChild(el);
el.style.display = 'none';
Chapter 05 — Testing: Micro & Macro Tasks
98. What are Microtasks?
They are queued up and executed at the
end of a task. No browser action in
between.
· MutationObserver callback (DOM
changes)
· Promises (even settled ones)
Chapter 05 — Testing: Micro & Macro Tasks
99. // endless (macro) tasks queue - is useless but okay
function cb() {
console.log('cb');
setTimeout(cb, 0)
}
cb();
//
⚠ ⚠ ⚠
This will hang your browser
// — save everything then try
"
function cb() {
console.log('cb');
Promise.resolve().then(cb);
}
cb();
Chapter 05 — Testing: Micro & Macro Tasks
109. Your tasks
· Stock Bug (Investigate)
· Stock Bug — Part 1, 2, 3
· Test RxJS w/ Oscar
"
— CPUs
· Test RxJS w/ Oscar
"
— Humans
· Can Oscar play multiple cards ?