SlideShare une entreprise Scribd logo
1  sur  53
Télécharger pour lire hors ligne
NG2 & NgRx/Store:
Oren Farhi @Orizens
hi!
I AM Oren Farhi
Senior Javascript Engineer
Freelance Javascript Consultant & Tutor
You can find me at:
orizens.com
github.com/orizens
@orizens
There’s Gotta Be Something Better
Redux for Angular 2 with NgRx/Store
- Problems of State Management
- What is NgRx/Store
- Usecase App - Echoes Player
NgRx/Store In Echoes Player Agenda
Problems With
State Management
The State Of The App?
◦ Where?
◦ Share
Current State Of The App
UI ⇔ Model
Solution: ngrx/store (benefits)
1. App State is Predictable
2. Separate Logics From UI
3. Optimization in Performance
4. Easy To Test
5. Time Travel (logs)
6. Route & State
7. Promotes Stateless Components
Ngrx/Store
State Management Made Easy
Example App: Echoes Player - http://echotu.be
Store
Mother Of All States
Store is an observable “DB”
Store
{ Data }
{ Object }
[ Array ]
number
string
{ Data }
{ Data }
{ Data }
Store - json object...
bootstrap(App, [
provideStore({ videos: videosReducer })
]);
Reducers - CRUD the Store with Pure Functions (Observable)
const videos = function(state = [], action) => {
switch (action.type) {
case 'ADD_VIDEO':
return state.concat( action.payload );
default:
return state;
}
}
Reducers - CRUD the Store with Pure Functions (Observable)
let sum = [1,2,3]
sum.reduce(
(result, value) => result + value,
0
);
Action - dispatch an update to State through Reducers
addVideo(media) {
this.store.dispatch({
type: ‘ADD_VIDEO’,
payload: media
});
}
Subscribe And Observe - data projection, slice store’s data
@Component({ selector: ‘youtube-player’ })
constructor (store) {
this.player$ = store.select(store => store.player);
this.player$.subscribe(player => {
this.isFullscreen = player.isFullscreen
});
}
Subscribe - data projection - listen with Observable
@Component({ selector: ‘youtube-player’ })
constructor (store) {
this.player$ = store.select(store => store.player);
this.player$.subscribe(player => {
this.isFullscreen = player.isFullscreen
});
}
Subscribe And Observe - data projection, slice store’s data
let initialPlayerState = {
mediaId: { videoId: 'NONE' },
media: {
snippet: { title: 'No Media Yet' }
},
showPlayer: true,
playerState: 0,
isFullscreen: false
}
UI Subscribe - “async” pipe - data projection to view
<section class="player">
<player-controls
[player]="player$ | async"
(action)="handleControlAction($event)"
></player-controls>
</section>
let tempObv = player$.subscribe(player => this.player = player);
tempObv.unsubscribe(); // when view is destroyed
One Way Data Flow
Component
Reducer
Store
Action
NgRx/Store in Echoes Player
Store & The Youtube Videos Component
Store In Echoes Player
export interface EchoesState {
videos: EchoesVideos;
player: YoutubePlayerState;
nowPlaylist: YoutubeMediaPlaylist;
user: UserProfile;
search: PlayerSearch;
}
Youtube Videos - SMART Component
<youtube-videos>
Youtube Videos - 2 Stateless Components (DUMB)
<youtube-videos>
<youtube-list>
<player-search>
Youtube Videos Component (SMART)
@Component({
selector: 'youtube-videos',
directives: [ PlayerSearchComponent, YoutubeList],
template: `
<player-search
[query]="search$ | async"
(change)="resetPageToken()"
(search)="search($event)"
></player-search>
<youtube-list
[list]="videos$ | async"
(play)="playSelectedVideo($event)"
(queue)="queueSelectedVideo($event)"
></youtube-list>`
})
export class YoutubeVideos implements OnInit {}
Youtube Videos Component (SMART)
@Component({
selector: 'youtube-videos',
directives: [ PlayerSearchComponent, YoutubeList],
template: `
<player-search
[query]="search$ | async"
(change)="resetPageToken()"
(search)="search($event)"
></player-search>
<youtube-list
[list]="videos$ | async"
(play)="playSelectedVideo($event)"
(queue)="queueSelectedVideo($event)"
></youtube-list>`
})
export class YoutubeVideos implements OnInit {}
Performance Boost For Components
@Component({
selector: 'youtube-list',
template: `
<youtube-media
*ngFor="let media of list"
[media]="media"
(play)="playSelectedVideo(media)"
(queue)="queueSelectedVideo(media)"
(add)="addVideo(media)">
</youtube-media>
`,
directives: [NgFor, YoutubeMedia ],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class YoutubeList {
@Input() list: Array<any>;
}
Youtube Videos Component (SMART)
@Component({
selector: 'youtube-videos',
directives: [ PlayerSearchComponent, YoutubeList],
template: `
<player-search
[query]="search$ | async"
(change)="resetPageToken()"
(search)="search($event)"
></player-search>
<youtube-list
[list]="videos$ | async"
(play)="playSelectedVideo($event)"
(queue)="queueSelectedVideo($event)"
></youtube-list>`
})
export class YoutubeVideos implements OnInit {}
Youtube Videos - Connecting to Store
export class YoutubeVideos implements OnInit {
videos$: Observable<EchoesVideos>;
search$: Observable<PlayerSearch>;
constructor(
private youtubeSearch: YoutubeSearch,
private nowPlaylistService: NowPlaylistService,
private store: Store<EchoesState>,
public youtubePlayer: YoutubePlayerService) {
this.videos$ = store.select(state => state.videos);
this.search$ = store.select(state => state.search);
}
}
Youtube Videos - Connecting to Store
export class YoutubeVideos implements OnInit {
videos$: Observable<EchoesVideos>;
search$: Observable<PlayerSearch>;
constructor(
private youtubeSearch: YoutubeSearch,
private nowPlaylistService: NowPlaylistService,
private store: Store<EchoesState>,
public youtubePlayer: YoutubePlayerService) {
this.videos$ = store.select(state => state.videos);
this.search$ = store.select(state => state.search);
}
}
Updating the Store
@Component({
selector: 'youtube-videos',
directives: [ PlayerSearchComponent, YoutubeList],
template: `
<player-search
[query]="search$ | async"
(change)="resetPageToken()"
(search)="search($event)"
></player-search>
<youtube-list
[list]="videos$ | async"
(play)="playSelectedVideo($event)"
(queue)="queueSelectedVideo($event)"
></youtube-list>`
})
export class YoutubeVideos implements OnInit {}
Youtube Videos Component
Interaction with store services:
- YoutubeSearch
- YoutubePlayer
- NowPlaylist
Youtube Videos - Updating The Store
export class YoutubeVideos implements OnInit {
videos$: Observable<EchoesVideos>;
playerSearch$: Observable<PlayerSearch>;
constructor(){ ... }
search (query: string) {
if (query.length) {
this.youtubeSearch.search(query, false);
}
}
queueSelectedVideo (media: GoogleApiYouTubeSearchResource) {
return this.nowPlaylistService.queueVideo(media.id.videoId);
}
}
Youtube Videos - Updating The Store
export class YoutubeVideos implements OnInit {
videos$: Observable<EchoesVideos>;
playerSearch$: Observable<PlayerSearch>;
constructor(){ ... }
search (query: string) {
if (query.length) {
this.youtubeSearch.search(query, false);
}
}
queueSelectedVideo (media: GoogleApiYouTubeSearchResource) {
return this.nowPlaylistService.queueVideo(media.id.videoId);
}
}
Services - dispatch to Store
@Injectable()
export class NowPlaylistService {
constructor(public store: Store<EchoesState>) {
this.playlist$ = this.store.select(state => state.nowPlaylist);
}
queueVideo (mediaId: string) {
return this.youtubeVideosInfo.api
.list(mediaId).then(response => {
this.store.dispatch({
type: QUEUE, //- const imported from reducer
payload: response.items[0]
});
return response.items[0];
});
}
}
NowPlaylist Reducer
let initialState = {
videos: [],
index: '',
filter: ''
}
export const nowPlaylist = (state = initialState, action) => {
switch (action.type) {
case NowPlaylistActions.QUEUE:
return Object.assign(
);
default:
return state;
}
NowPlaylist Reducer
let initialState = {
videos: [],
index: '',
filter: ''
}
export const nowPlaylist = (state = initialState, action) => {
switch (action.type) {
case NowPlaylistActions.QUEUE:
return Object.assign({}, state, {
videos: addMedia(state.videos, action.payload)
});
default:
return state;
}
Services - dispatch to Store
@Injectable()
export class NowPlaylistService {
constructor(public store: Store<EchoesState>) {
this.playlist$ = this.store.select(state => state.nowPlaylist);
}
queueVideo (mediaId: string) {
return this.youtubeVideosInfo.api
.list(mediaId).then(response => {
this.store.dispatch({
type: QUEUE, //- const imported from reducer
payload: response.items[0]
});
return response.items[0];
});
}
}
Ngrx/effects
Side effects of Actions
Side Effects for Queue Video
@Injectable()
export class NowPlaylistEffects {
constructor(
store$,
nowPlaylistActions
youtubeVideosInfo
){}
@Effect() queueVideoReady$ = this.store$
.whenAction(NowPlaylistActions.QUEUE_LOAD_VIDEO)
.map<GoogleApiYouTubeSearchResource>(toPayload)
.switchMap(media => this.youtubeVideosInfo.fetchVideoData(media.id.
videoId)
.map(media => this.nowPlaylistActions.queueVideo(media))
.catch(() => Observable.of(this.nowPlaylistActions.queueFailed(media)))
);
}
Testing Reducers
Testing now playlist
Loading the tested objects
import {
it,
inject,
async,
describe,
expect
} from '@angular/core/testing';
import { nowPlaylist, NowPlaylistActions } from './now-playlist';
import { YoutubeMediaItemsMock } from './mocks/youtube.media.
items';
Spec - select a video in now playlist
it('should select the chosen video', () => {
const state = { index: '', videos: [...YoutubeMediaItemsMock], filter: '' };
const actual = nowPlaylist(state, {
type: NowPlaylistActions.SELECT,
payload: YoutubeMediaItemsMock[0]
});
const expected = YoutubeMediaItemsMock[0];
expect(actual.index).toBe(expected.id);
});
Spec - set initial state
it('should select the chosen video', () => {
const state = { index: '', videos: [...YoutubeMediaItemsMock], filter: '' };
const actual = nowPlaylist(state, {
type: NowPlaylistActions.SELECT,
payload: YoutubeMediaItemsMock[0]
});
const expected = YoutubeMediaItemsMock[0];
expect(actual.index).toBe(expected.id);
});
Spec - select a video in now playlist
it('should select the chosen video', () => {
const state = { index: '', videos: [...YoutubeMediaItemsMock], filter: '' };
const actual = nowPlaylist(state, {
type: NowPlaylistActions.SELECT,
payload: YoutubeMediaItemsMock[0]
});
const expected = YoutubeMediaItemsMock[0];
expect(actual.index).toBe(expected.id);
});
Spec - select a video in now playlist
it('should select the chosen video', () => {
const state = { index: '', videos: [...YoutubeMediaItemsMock], filter: '' };
const actual = nowPlaylist(state, {
type: NowPlaylistActions.SELECT,
payload: YoutubeMediaItemsMock[0]
});
const expected = YoutubeMediaItemsMock[0];
expect(actual.index).toBe(expected.id);
});
Spec - select a video in now playlist
it('should select the chosen video', () => {
const state = { index: '', videos: [...YoutubeMediaItemsMock], filter: '' };
const actual = nowPlaylist(state, {
type: NowPlaylistActions.SELECT,
payload: YoutubeMediaItemsMock[0]
});
const expected = YoutubeMediaItemsMock[0];
expect(actual.index).toBe(expected.id);
});
Ngrx DevTools
Ngrx DevTools
More NgRx To Explore:
ngrx/router
ngrx/db
...
Thanks!
ANY QUESTIONS?
You can find me at
@orizens
oren@orizens.com
http://orizens.com/services
NG2 + Ngrx/Store Workshop:
Register at http://goo.gl/EJmm7q
CREDITS
◦ Presentation template by SlidesCarnival
◦ http://orizens.com/wp/topics/adding-redux-with-
ngrxstore-to-angular-2-part-1/
◦ http://orizens.com/wp/topics/adding-redux-with-
ngrxstore-to-angular2-part-2-testing-reducers/
◦ http://orizens.com/wp/topics/angular-2-ngrxstore-
the-ngmodel-in-between-use-case-from-angular-1/
◦ Comprehensive Introduction to NgRx/Store by
btroncone
◦ Reactive Angular With Ngrx/Store by Rob Warmald
◦ https://github.com/ngrx/store
◦

Contenu connexe

Tendances

Workshop 20: ReactJS Part II Flux Pattern & Redux
Workshop 20: ReactJS Part II Flux Pattern & ReduxWorkshop 20: ReactJS Part II Flux Pattern & Redux
Workshop 20: ReactJS Part II Flux Pattern & ReduxVisual Engineering
 
Introduction to React & Redux
Introduction to React & ReduxIntroduction to React & Redux
Introduction to React & ReduxBoris Dinkevich
 
Workshop 14: AngularJS Parte III
Workshop 14: AngularJS Parte IIIWorkshop 14: AngularJS Parte III
Workshop 14: AngularJS Parte IIIVisual Engineering
 
Redux pattens - JSHeroes 2018
Redux pattens - JSHeroes 2018Redux pattens - JSHeroes 2018
Redux pattens - JSHeroes 2018Nir Kaufman
 
Using React, Redux and Saga with Lottoland APIs
Using React, Redux and Saga with Lottoland APIsUsing React, Redux and Saga with Lottoland APIs
Using React, Redux and Saga with Lottoland APIsMihail Gaberov
 
Boosting Angular runtime performance
Boosting Angular runtime performanceBoosting Angular runtime performance
Boosting Angular runtime performanceNir Kaufman
 
Quick start with React | DreamLab Academy #2
Quick start with React | DreamLab Academy #2Quick start with React | DreamLab Academy #2
Quick start with React | DreamLab Academy #2DreamLab
 
ReactJs presentation
ReactJs presentationReactJs presentation
ReactJs presentationnishasowdri
 
Workshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte IIWorkshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte IIVisual Engineering
 
Switch to React.js from AngularJS developer
Switch to React.js from AngularJS developerSwitch to React.js from AngularJS developer
Switch to React.js from AngularJS developerEugene Zharkov
 
Creating a WYSIWYG Editor with React
Creating a WYSIWYG Editor with ReactCreating a WYSIWYG Editor with React
Creating a WYSIWYG Editor with Reactpeychevi
 
Modern Web Developement
Modern Web DevelopementModern Web Developement
Modern Web Developementpeychevi
 
Workshop 19: ReactJS Introduction
Workshop 19: ReactJS IntroductionWorkshop 19: ReactJS Introduction
Workshop 19: ReactJS IntroductionVisual Engineering
 
Asyc flow control with javascript generators - redux-saga
Asyc flow control with javascript generators - redux-sagaAsyc flow control with javascript generators - redux-saga
Asyc flow control with javascript generators - redux-sagaPedro Solá
 
Redux training
Redux trainingRedux training
Redux trainingdasersoft
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingVisual Engineering
 

Tendances (20)

Workshop 20: ReactJS Part II Flux Pattern & Redux
Workshop 20: ReactJS Part II Flux Pattern & ReduxWorkshop 20: ReactJS Part II Flux Pattern & Redux
Workshop 20: ReactJS Part II Flux Pattern & Redux
 
Introduction to React & Redux
Introduction to React & ReduxIntroduction to React & Redux
Introduction to React & Redux
 
Workshop 14: AngularJS Parte III
Workshop 14: AngularJS Parte IIIWorkshop 14: AngularJS Parte III
Workshop 14: AngularJS Parte III
 
Redux pattens - JSHeroes 2018
Redux pattens - JSHeroes 2018Redux pattens - JSHeroes 2018
Redux pattens - JSHeroes 2018
 
Using React, Redux and Saga with Lottoland APIs
Using React, Redux and Saga with Lottoland APIsUsing React, Redux and Saga with Lottoland APIs
Using React, Redux and Saga with Lottoland APIs
 
Boosting Angular runtime performance
Boosting Angular runtime performanceBoosting Angular runtime performance
Boosting Angular runtime performance
 
React redux
React reduxReact redux
React redux
 
Quick start with React | DreamLab Academy #2
Quick start with React | DreamLab Academy #2Quick start with React | DreamLab Academy #2
Quick start with React | DreamLab Academy #2
 
ReactJs presentation
ReactJs presentationReactJs presentation
ReactJs presentation
 
Workshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte IIWorkshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte II
 
React & Redux
React & ReduxReact & Redux
React & Redux
 
Switch to React.js from AngularJS developer
Switch to React.js from AngularJS developerSwitch to React.js from AngularJS developer
Switch to React.js from AngularJS developer
 
Creating a WYSIWYG Editor with React
Creating a WYSIWYG Editor with ReactCreating a WYSIWYG Editor with React
Creating a WYSIWYG Editor with React
 
Modern Web Developement
Modern Web DevelopementModern Web Developement
Modern Web Developement
 
Workshop 19: ReactJS Introduction
Workshop 19: ReactJS IntroductionWorkshop 19: ReactJS Introduction
Workshop 19: ReactJS Introduction
 
Asyc flow control with javascript generators - redux-saga
Asyc flow control with javascript generators - redux-sagaAsyc flow control with javascript generators - redux-saga
Asyc flow control with javascript generators - redux-saga
 
React&redux
React&reduxReact&redux
React&redux
 
Redux training
Redux trainingRedux training
Redux training
 
React with Redux
React with ReduxReact with Redux
React with Redux
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
 

En vedette

redux and angular - up and running
redux and angular - up and runningredux and angular - up and running
redux and angular - up and runningNir Kaufman
 
Redux data flow with angular 2
Redux data flow with angular 2Redux data flow with angular 2
Redux data flow with angular 2Gil Fink
 
Redux in Angular2 for jsbe
Redux in Angular2 for jsbeRedux in Angular2 for jsbe
Redux in Angular2 for jsbeBrecht Billiet
 
Angular Promises and Advanced Routing
Angular Promises and Advanced RoutingAngular Promises and Advanced Routing
Angular Promises and Advanced RoutingAlexe Bogdan
 
Functional Reactive Angular 2
Functional Reactive Angular 2 Functional Reactive Angular 2
Functional Reactive Angular 2 Tomasz Bak
 
Progressive web apps with Angular 2
Progressive web apps with Angular 2Progressive web apps with Angular 2
Progressive web apps with Angular 2Manfred Steyer
 

En vedette (7)

redux and angular - up and running
redux and angular - up and runningredux and angular - up and running
redux and angular - up and running
 
Redux data flow with angular 2
Redux data flow with angular 2Redux data flow with angular 2
Redux data flow with angular 2
 
Redux in Angular2 for jsbe
Redux in Angular2 for jsbeRedux in Angular2 for jsbe
Redux in Angular2 for jsbe
 
Angular Promises and Advanced Routing
Angular Promises and Advanced RoutingAngular Promises and Advanced Routing
Angular Promises and Advanced Routing
 
Angular redux
Angular reduxAngular redux
Angular redux
 
Functional Reactive Angular 2
Functional Reactive Angular 2 Functional Reactive Angular 2
Functional Reactive Angular 2
 
Progressive web apps with Angular 2
Progressive web apps with Angular 2Progressive web apps with Angular 2
Progressive web apps with Angular 2
 

Similaire à Angular2 & ngrx/store: Game of States

We sport architecture_implementation
We sport architecture_implementationWe sport architecture_implementation
We sport architecture_implementationaurelianaur
 
Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgetsvelveeta_512
 
Asynchronous Programming at Netflix
Asynchronous Programming at NetflixAsynchronous Programming at Netflix
Asynchronous Programming at NetflixC4Media
 
How to create a magento controller in magento extension
How to create a magento controller in magento extensionHow to create a magento controller in magento extension
How to create a magento controller in magento extensionHendy Irawan
 
A re introduction to webpack - reactfoo - mumbai
A re introduction to webpack - reactfoo - mumbaiA re introduction to webpack - reactfoo - mumbai
A re introduction to webpack - reactfoo - mumbaiPraveen Puglia
 
준비하세요 Angular js 2.0
준비하세요 Angular js 2.0준비하세요 Angular js 2.0
준비하세요 Angular js 2.0Jeado Ko
 
React 16: new features and beyond
React 16: new features and beyondReact 16: new features and beyond
React 16: new features and beyondArtjoker
 
Backbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCBackbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCpootsbook
 
Building Video Applications with YouTube APIs
Building Video Applications with YouTube APIsBuilding Video Applications with YouTube APIs
Building Video Applications with YouTube APIsJarek Wilkiewicz
 
Magento Live Australia 2016: Request Flow
Magento Live Australia 2016: Request FlowMagento Live Australia 2016: Request Flow
Magento Live Australia 2016: Request FlowVrann Tulika
 
Enhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentEnhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentYao Nien Chung
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207patter
 
Seven Peaks Speaks - Compose Screenshot Testing Made Easy
Seven Peaks Speaks - Compose Screenshot Testing Made EasySeven Peaks Speaks - Compose Screenshot Testing Made Easy
Seven Peaks Speaks - Compose Screenshot Testing Made EasySeven Peaks Speaks
 
Ditching JQuery
Ditching JQueryDitching JQuery
Ditching JQueryhowlowck
 
Dive into React Performance
Dive into React PerformanceDive into React Performance
Dive into React PerformanceChing Ting Wu
 
Modules and injector
Modules and injectorModules and injector
Modules and injectorEyal Vardi
 
How to perform debounce in react
How to perform debounce in reactHow to perform debounce in react
How to perform debounce in reactBOSC Tech Labs
 

Similaire à Angular2 & ngrx/store: Game of States (20)

How to React Native
How to React NativeHow to React Native
How to React Native
 
We sport architecture_implementation
We sport architecture_implementationWe sport architecture_implementation
We sport architecture_implementation
 
Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgets
 
Asynchronous Programming at Netflix
Asynchronous Programming at NetflixAsynchronous Programming at Netflix
Asynchronous Programming at Netflix
 
Advanced redux
Advanced reduxAdvanced redux
Advanced redux
 
How to create a magento controller in magento extension
How to create a magento controller in magento extensionHow to create a magento controller in magento extension
How to create a magento controller in magento extension
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
A re introduction to webpack - reactfoo - mumbai
A re introduction to webpack - reactfoo - mumbaiA re introduction to webpack - reactfoo - mumbai
A re introduction to webpack - reactfoo - mumbai
 
준비하세요 Angular js 2.0
준비하세요 Angular js 2.0준비하세요 Angular js 2.0
준비하세요 Angular js 2.0
 
React 16: new features and beyond
React 16: new features and beyondReact 16: new features and beyond
React 16: new features and beyond
 
Backbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCBackbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVC
 
Building Video Applications with YouTube APIs
Building Video Applications with YouTube APIsBuilding Video Applications with YouTube APIs
Building Video Applications with YouTube APIs
 
Magento Live Australia 2016: Request Flow
Magento Live Australia 2016: Request FlowMagento Live Australia 2016: Request Flow
Magento Live Australia 2016: Request Flow
 
Enhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentEnhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order component
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
 
Seven Peaks Speaks - Compose Screenshot Testing Made Easy
Seven Peaks Speaks - Compose Screenshot Testing Made EasySeven Peaks Speaks - Compose Screenshot Testing Made Easy
Seven Peaks Speaks - Compose Screenshot Testing Made Easy
 
Ditching JQuery
Ditching JQueryDitching JQuery
Ditching JQuery
 
Dive into React Performance
Dive into React PerformanceDive into React Performance
Dive into React Performance
 
Modules and injector
Modules and injectorModules and injector
Modules and injector
 
How to perform debounce in react
How to perform debounce in reactHow to perform debounce in react
How to perform debounce in react
 

Dernier

『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书rnrncn29
 
PHP-based rendering of TYPO3 Documentation
PHP-based rendering of TYPO3 DocumentationPHP-based rendering of TYPO3 Documentation
PHP-based rendering of TYPO3 DocumentationLinaWolf1
 
办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一
办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一
办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一z xss
 
SCM Symposium PPT Format Customer loyalty is predi
SCM Symposium PPT Format Customer loyalty is prediSCM Symposium PPT Format Customer loyalty is predi
SCM Symposium PPT Format Customer loyalty is predieusebiomeyer
 
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书zdzoqco
 
Film cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasaFilm cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasa494f574xmv
 
定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一
定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一
定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一Fs
 
Top 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptxTop 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptxDyna Gilbert
 
A Good Girl's Guide to Murder (A Good Girl's Guide to Murder, #1)
A Good Girl's Guide to Murder (A Good Girl's Guide to Murder, #1)A Good Girl's Guide to Murder (A Good Girl's Guide to Murder, #1)
A Good Girl's Guide to Murder (A Good Girl's Guide to Murder, #1)Christopher H Felton
 
Q4-1-Illustrating-Hypothesis-Testing.pptx
Q4-1-Illustrating-Hypothesis-Testing.pptxQ4-1-Illustrating-Hypothesis-Testing.pptx
Q4-1-Illustrating-Hypothesis-Testing.pptxeditsforyah
 
Contact Rya Baby for Call Girls New Delhi
Contact Rya Baby for Call Girls New DelhiContact Rya Baby for Call Girls New Delhi
Contact Rya Baby for Call Girls New Delhimiss dipika
 
Font Performance - NYC WebPerf Meetup April '24
Font Performance - NYC WebPerf Meetup April '24Font Performance - NYC WebPerf Meetup April '24
Font Performance - NYC WebPerf Meetup April '24Paul Calvano
 
Git and Github workshop GDSC MLRITM
Git and Github  workshop GDSC MLRITMGit and Github  workshop GDSC MLRITM
Git and Github workshop GDSC MLRITMgdsc13
 
Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作
Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作
Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作ys8omjxb
 
定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一
定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一
定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一Fs
 
定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一
定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一
定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一Fs
 
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书rnrncn29
 
Call Girls In The Ocean Pearl Retreat Hotel New Delhi 9873777170
Call Girls In The Ocean Pearl Retreat Hotel New Delhi 9873777170Call Girls In The Ocean Pearl Retreat Hotel New Delhi 9873777170
Call Girls In The Ocean Pearl Retreat Hotel New Delhi 9873777170Sonam Pathan
 
Blepharitis inflammation of eyelid symptoms cause everything included along w...
Blepharitis inflammation of eyelid symptoms cause everything included along w...Blepharitis inflammation of eyelid symptoms cause everything included along w...
Blepharitis inflammation of eyelid symptoms cause everything included along w...Excelmac1
 

Dernier (20)

『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
 
PHP-based rendering of TYPO3 Documentation
PHP-based rendering of TYPO3 DocumentationPHP-based rendering of TYPO3 Documentation
PHP-based rendering of TYPO3 Documentation
 
办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一
办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一
办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一
 
SCM Symposium PPT Format Customer loyalty is predi
SCM Symposium PPT Format Customer loyalty is prediSCM Symposium PPT Format Customer loyalty is predi
SCM Symposium PPT Format Customer loyalty is predi
 
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
 
Film cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasaFilm cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasa
 
定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一
定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一
定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一
 
Top 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptxTop 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptx
 
A Good Girl's Guide to Murder (A Good Girl's Guide to Murder, #1)
A Good Girl's Guide to Murder (A Good Girl's Guide to Murder, #1)A Good Girl's Guide to Murder (A Good Girl's Guide to Murder, #1)
A Good Girl's Guide to Murder (A Good Girl's Guide to Murder, #1)
 
Q4-1-Illustrating-Hypothesis-Testing.pptx
Q4-1-Illustrating-Hypothesis-Testing.pptxQ4-1-Illustrating-Hypothesis-Testing.pptx
Q4-1-Illustrating-Hypothesis-Testing.pptx
 
Contact Rya Baby for Call Girls New Delhi
Contact Rya Baby for Call Girls New DelhiContact Rya Baby for Call Girls New Delhi
Contact Rya Baby for Call Girls New Delhi
 
Font Performance - NYC WebPerf Meetup April '24
Font Performance - NYC WebPerf Meetup April '24Font Performance - NYC WebPerf Meetup April '24
Font Performance - NYC WebPerf Meetup April '24
 
Git and Github workshop GDSC MLRITM
Git and Github  workshop GDSC MLRITMGit and Github  workshop GDSC MLRITM
Git and Github workshop GDSC MLRITM
 
young call girls in Uttam Nagar🔝 9953056974 🔝 Delhi escort Service
young call girls in Uttam Nagar🔝 9953056974 🔝 Delhi escort Serviceyoung call girls in Uttam Nagar🔝 9953056974 🔝 Delhi escort Service
young call girls in Uttam Nagar🔝 9953056974 🔝 Delhi escort Service
 
Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作
Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作
Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作
 
定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一
定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一
定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一
 
定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一
定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一
定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一
 
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
 
Call Girls In The Ocean Pearl Retreat Hotel New Delhi 9873777170
Call Girls In The Ocean Pearl Retreat Hotel New Delhi 9873777170Call Girls In The Ocean Pearl Retreat Hotel New Delhi 9873777170
Call Girls In The Ocean Pearl Retreat Hotel New Delhi 9873777170
 
Blepharitis inflammation of eyelid symptoms cause everything included along w...
Blepharitis inflammation of eyelid symptoms cause everything included along w...Blepharitis inflammation of eyelid symptoms cause everything included along w...
Blepharitis inflammation of eyelid symptoms cause everything included along w...
 

Angular2 & ngrx/store: Game of States

  • 1. NG2 & NgRx/Store: Oren Farhi @Orizens
  • 2. hi! I AM Oren Farhi Senior Javascript Engineer Freelance Javascript Consultant & Tutor You can find me at: orizens.com github.com/orizens @orizens
  • 3. There’s Gotta Be Something Better Redux for Angular 2 with NgRx/Store
  • 4. - Problems of State Management - What is NgRx/Store - Usecase App - Echoes Player NgRx/Store In Echoes Player Agenda
  • 6. The State Of The App? ◦ Where? ◦ Share
  • 7. Current State Of The App UI ⇔ Model
  • 8. Solution: ngrx/store (benefits) 1. App State is Predictable 2. Separate Logics From UI 3. Optimization in Performance 4. Easy To Test 5. Time Travel (logs) 6. Route & State 7. Promotes Stateless Components
  • 10. Example App: Echoes Player - http://echotu.be
  • 12. Store is an observable “DB” Store { Data } { Object } [ Array ] number string { Data } { Data } { Data }
  • 13. Store - json object... bootstrap(App, [ provideStore({ videos: videosReducer }) ]);
  • 14. Reducers - CRUD the Store with Pure Functions (Observable) const videos = function(state = [], action) => { switch (action.type) { case 'ADD_VIDEO': return state.concat( action.payload ); default: return state; } }
  • 15. Reducers - CRUD the Store with Pure Functions (Observable) let sum = [1,2,3] sum.reduce( (result, value) => result + value, 0 );
  • 16. Action - dispatch an update to State through Reducers addVideo(media) { this.store.dispatch({ type: ‘ADD_VIDEO’, payload: media }); }
  • 17. Subscribe And Observe - data projection, slice store’s data @Component({ selector: ‘youtube-player’ }) constructor (store) { this.player$ = store.select(store => store.player); this.player$.subscribe(player => { this.isFullscreen = player.isFullscreen }); }
  • 18. Subscribe - data projection - listen with Observable @Component({ selector: ‘youtube-player’ }) constructor (store) { this.player$ = store.select(store => store.player); this.player$.subscribe(player => { this.isFullscreen = player.isFullscreen }); }
  • 19. Subscribe And Observe - data projection, slice store’s data let initialPlayerState = { mediaId: { videoId: 'NONE' }, media: { snippet: { title: 'No Media Yet' } }, showPlayer: true, playerState: 0, isFullscreen: false }
  • 20. UI Subscribe - “async” pipe - data projection to view <section class="player"> <player-controls [player]="player$ | async" (action)="handleControlAction($event)" ></player-controls> </section> let tempObv = player$.subscribe(player => this.player = player); tempObv.unsubscribe(); // when view is destroyed
  • 21. One Way Data Flow Component Reducer Store Action
  • 22. NgRx/Store in Echoes Player Store & The Youtube Videos Component
  • 23. Store In Echoes Player export interface EchoesState { videos: EchoesVideos; player: YoutubePlayerState; nowPlaylist: YoutubeMediaPlaylist; user: UserProfile; search: PlayerSearch; }
  • 24. Youtube Videos - SMART Component <youtube-videos>
  • 25. Youtube Videos - 2 Stateless Components (DUMB) <youtube-videos> <youtube-list> <player-search>
  • 26. Youtube Videos Component (SMART) @Component({ selector: 'youtube-videos', directives: [ PlayerSearchComponent, YoutubeList], template: ` <player-search [query]="search$ | async" (change)="resetPageToken()" (search)="search($event)" ></player-search> <youtube-list [list]="videos$ | async" (play)="playSelectedVideo($event)" (queue)="queueSelectedVideo($event)" ></youtube-list>` }) export class YoutubeVideos implements OnInit {}
  • 27. Youtube Videos Component (SMART) @Component({ selector: 'youtube-videos', directives: [ PlayerSearchComponent, YoutubeList], template: ` <player-search [query]="search$ | async" (change)="resetPageToken()" (search)="search($event)" ></player-search> <youtube-list [list]="videos$ | async" (play)="playSelectedVideo($event)" (queue)="queueSelectedVideo($event)" ></youtube-list>` }) export class YoutubeVideos implements OnInit {}
  • 28. Performance Boost For Components @Component({ selector: 'youtube-list', template: ` <youtube-media *ngFor="let media of list" [media]="media" (play)="playSelectedVideo(media)" (queue)="queueSelectedVideo(media)" (add)="addVideo(media)"> </youtube-media> `, directives: [NgFor, YoutubeMedia ], changeDetection: ChangeDetectionStrategy.OnPush }) export class YoutubeList { @Input() list: Array<any>; }
  • 29. Youtube Videos Component (SMART) @Component({ selector: 'youtube-videos', directives: [ PlayerSearchComponent, YoutubeList], template: ` <player-search [query]="search$ | async" (change)="resetPageToken()" (search)="search($event)" ></player-search> <youtube-list [list]="videos$ | async" (play)="playSelectedVideo($event)" (queue)="queueSelectedVideo($event)" ></youtube-list>` }) export class YoutubeVideos implements OnInit {}
  • 30. Youtube Videos - Connecting to Store export class YoutubeVideos implements OnInit { videos$: Observable<EchoesVideos>; search$: Observable<PlayerSearch>; constructor( private youtubeSearch: YoutubeSearch, private nowPlaylistService: NowPlaylistService, private store: Store<EchoesState>, public youtubePlayer: YoutubePlayerService) { this.videos$ = store.select(state => state.videos); this.search$ = store.select(state => state.search); } }
  • 31. Youtube Videos - Connecting to Store export class YoutubeVideos implements OnInit { videos$: Observable<EchoesVideos>; search$: Observable<PlayerSearch>; constructor( private youtubeSearch: YoutubeSearch, private nowPlaylistService: NowPlaylistService, private store: Store<EchoesState>, public youtubePlayer: YoutubePlayerService) { this.videos$ = store.select(state => state.videos); this.search$ = store.select(state => state.search); } }
  • 32. Updating the Store @Component({ selector: 'youtube-videos', directives: [ PlayerSearchComponent, YoutubeList], template: ` <player-search [query]="search$ | async" (change)="resetPageToken()" (search)="search($event)" ></player-search> <youtube-list [list]="videos$ | async" (play)="playSelectedVideo($event)" (queue)="queueSelectedVideo($event)" ></youtube-list>` }) export class YoutubeVideos implements OnInit {}
  • 33. Youtube Videos Component Interaction with store services: - YoutubeSearch - YoutubePlayer - NowPlaylist
  • 34. Youtube Videos - Updating The Store export class YoutubeVideos implements OnInit { videos$: Observable<EchoesVideos>; playerSearch$: Observable<PlayerSearch>; constructor(){ ... } search (query: string) { if (query.length) { this.youtubeSearch.search(query, false); } } queueSelectedVideo (media: GoogleApiYouTubeSearchResource) { return this.nowPlaylistService.queueVideo(media.id.videoId); } }
  • 35. Youtube Videos - Updating The Store export class YoutubeVideos implements OnInit { videos$: Observable<EchoesVideos>; playerSearch$: Observable<PlayerSearch>; constructor(){ ... } search (query: string) { if (query.length) { this.youtubeSearch.search(query, false); } } queueSelectedVideo (media: GoogleApiYouTubeSearchResource) { return this.nowPlaylistService.queueVideo(media.id.videoId); } }
  • 36. Services - dispatch to Store @Injectable() export class NowPlaylistService { constructor(public store: Store<EchoesState>) { this.playlist$ = this.store.select(state => state.nowPlaylist); } queueVideo (mediaId: string) { return this.youtubeVideosInfo.api .list(mediaId).then(response => { this.store.dispatch({ type: QUEUE, //- const imported from reducer payload: response.items[0] }); return response.items[0]; }); } }
  • 37. NowPlaylist Reducer let initialState = { videos: [], index: '', filter: '' } export const nowPlaylist = (state = initialState, action) => { switch (action.type) { case NowPlaylistActions.QUEUE: return Object.assign( ); default: return state; }
  • 38. NowPlaylist Reducer let initialState = { videos: [], index: '', filter: '' } export const nowPlaylist = (state = initialState, action) => { switch (action.type) { case NowPlaylistActions.QUEUE: return Object.assign({}, state, { videos: addMedia(state.videos, action.payload) }); default: return state; }
  • 39. Services - dispatch to Store @Injectable() export class NowPlaylistService { constructor(public store: Store<EchoesState>) { this.playlist$ = this.store.select(state => state.nowPlaylist); } queueVideo (mediaId: string) { return this.youtubeVideosInfo.api .list(mediaId).then(response => { this.store.dispatch({ type: QUEUE, //- const imported from reducer payload: response.items[0] }); return response.items[0]; }); } }
  • 41. Side Effects for Queue Video @Injectable() export class NowPlaylistEffects { constructor( store$, nowPlaylistActions youtubeVideosInfo ){} @Effect() queueVideoReady$ = this.store$ .whenAction(NowPlaylistActions.QUEUE_LOAD_VIDEO) .map<GoogleApiYouTubeSearchResource>(toPayload) .switchMap(media => this.youtubeVideosInfo.fetchVideoData(media.id. videoId) .map(media => this.nowPlaylistActions.queueVideo(media)) .catch(() => Observable.of(this.nowPlaylistActions.queueFailed(media))) ); }
  • 43. Loading the tested objects import { it, inject, async, describe, expect } from '@angular/core/testing'; import { nowPlaylist, NowPlaylistActions } from './now-playlist'; import { YoutubeMediaItemsMock } from './mocks/youtube.media. items';
  • 44. Spec - select a video in now playlist it('should select the chosen video', () => { const state = { index: '', videos: [...YoutubeMediaItemsMock], filter: '' }; const actual = nowPlaylist(state, { type: NowPlaylistActions.SELECT, payload: YoutubeMediaItemsMock[0] }); const expected = YoutubeMediaItemsMock[0]; expect(actual.index).toBe(expected.id); });
  • 45. Spec - set initial state it('should select the chosen video', () => { const state = { index: '', videos: [...YoutubeMediaItemsMock], filter: '' }; const actual = nowPlaylist(state, { type: NowPlaylistActions.SELECT, payload: YoutubeMediaItemsMock[0] }); const expected = YoutubeMediaItemsMock[0]; expect(actual.index).toBe(expected.id); });
  • 46. Spec - select a video in now playlist it('should select the chosen video', () => { const state = { index: '', videos: [...YoutubeMediaItemsMock], filter: '' }; const actual = nowPlaylist(state, { type: NowPlaylistActions.SELECT, payload: YoutubeMediaItemsMock[0] }); const expected = YoutubeMediaItemsMock[0]; expect(actual.index).toBe(expected.id); });
  • 47. Spec - select a video in now playlist it('should select the chosen video', () => { const state = { index: '', videos: [...YoutubeMediaItemsMock], filter: '' }; const actual = nowPlaylist(state, { type: NowPlaylistActions.SELECT, payload: YoutubeMediaItemsMock[0] }); const expected = YoutubeMediaItemsMock[0]; expect(actual.index).toBe(expected.id); });
  • 48. Spec - select a video in now playlist it('should select the chosen video', () => { const state = { index: '', videos: [...YoutubeMediaItemsMock], filter: '' }; const actual = nowPlaylist(state, { type: NowPlaylistActions.SELECT, payload: YoutubeMediaItemsMock[0] }); const expected = YoutubeMediaItemsMock[0]; expect(actual.index).toBe(expected.id); });
  • 51. More NgRx To Explore: ngrx/router ngrx/db ...
  • 52. Thanks! ANY QUESTIONS? You can find me at @orizens oren@orizens.com http://orizens.com/services NG2 + Ngrx/Store Workshop: Register at http://goo.gl/EJmm7q
  • 53. CREDITS ◦ Presentation template by SlidesCarnival ◦ http://orizens.com/wp/topics/adding-redux-with- ngrxstore-to-angular-2-part-1/ ◦ http://orizens.com/wp/topics/adding-redux-with- ngrxstore-to-angular2-part-2-testing-reducers/ ◦ http://orizens.com/wp/topics/angular-2-ngrxstore- the-ngmodel-in-between-use-case-from-angular-1/ ◦ Comprehensive Introduction to NgRx/Store by btroncone ◦ Reactive Angular With Ngrx/Store by Rob Warmald ◦ https://github.com/ngrx/store ◦