What is one of the harder things, if not the hardest, you have done in your Angular applications?
STATE
STATE
Local state management
start small, start local (it is kind of a "must")
Chau Tran
...and more (who doesn't like to own a State Management library)
〞
When should we use state management?
– Any Angular developer ever
〞
Do I need to use a Global Store ABorC?
– Any Angular developer ever
FeatureA
FeatureB
FeatureC
StateA
StateB
StateC
App
FeatureA
FeatureB
FeatureC
StateA
StateB
StateC
App
FeatureA
FeatureB
FeatureC
StateA
StateB
StateC
App
FeatureA
FeatureB
FeatureC
StateA
StateB
StateC
App
import {Component, OnInit} from '@angular/core';
import {Hero} from '../hero';
import {HeroService} from '../hero.service';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: [ './dashboard.component.css' ]
})
export class DashboardComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService) { }
ngOnInit(): void {
this.getHeroes();
}
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
}
}
<div>
<label for="new-hero">Hero name: </label>
<input id="new-hero" #heroName />
<!-- (click) passes input value to add() and then clears the input -->
<button type="button" class="add-button" (click)="add(heroName.value); heroName.value=''">
Add hero
</button>
</div>
<h2>Top Heroes</h2>
<div class="heroes-menu">
<a *ngFor="let hero of heroes"
routerLink="/detail/{{hero.id}}">
{{hero.name}}
</a>
</div>
<app-hero-search></app-hero-search>
export class DashboardComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService) { }
ngOnInit(): void {
this.getHeroes();
}
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
}
add(name: string) {
name = name.trim();
if (!name) { return; }
this.heroService.addHero({ name } as Hero)
.subscribe(hero => {
this.heroes.push(hero);
});
}
}
export class DashboardComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService) { }
ngOnInit(): void {
this.getHeroes();
}
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
}
add(name: string) {
name = name.trim();
if (!name) { return; }
this.heroService.addHero({ name } as Hero)
.subscribe(hero => {
this.heroes.push(hero);
});
}
}
export class DashboardComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService) { }
ngOnInit(): void {
this.getHeroes();
}
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
}
add(name: string) {
name = name.trim();
if (!name) { return; }
this.heroService.addHero({ name } as Hero)
.subscribe(hero => {
this.heroes.push(hero);
});
}
}
export class DashboardComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService) { }
ngOnInit(): void {
this.getHeroes();
}
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
}
add(name: string) {
name = name.trim();
if (!name) { return; }
this.heroService.addHero({ name } as Hero)
.subscribe(hero => {
this.heroes.push(hero);
});
}
}
Pull-based state management
PUSH-based state management
export class SomeStateService {
private readonly $data = new BehaviorSubject<SomeData[]>([]);
readonly data$ = this.$data.asObservable();
private readonly $loading = new BehaviorSubject<boolean>(false);
readonly loading$ = this.$loading.asObservable();
constructor(private readonly someService: SomeService) {}
getData() {
this.$loading.next(true);
this.someService.getData().subscribe({
next: data => {
this.$data.next(data);
this.$loading.next(false);
},
error: error => {
console.log(error);
this.$loading.next(false);
},
})
}
addData(item) {
this.$loading.next(true);
this.someService.addData(item).subscribe({
next: addedItem => {
this.$data.next([...this.$data.value, addedItem]);
this.$loading.next(false);
},
error: error => {
console.log(error);
this.$loading.next(false);
},
})
}
filter(query) {
this.$data.next(this.$data.value.filter(...));
}
}
ngrx/component-store
ngrx/component-store
Read
Write
Side-effect
select
updater
effect
@ngrx/store
createSelector
createReducer
createReducer
createEffect
ngrx/component-store
Demo
ngrx/component-store
Demo
ngrx/component-store
no subscriptions
ngrx/component-store
race-conditions built-in
ngrx/component-store
easy to graduate to a global store if needed
ngrx/component-store
easier to test too
Q/A
Local State
By Chau Tran
Local State
- 397