by Gerard Sans | @gerardsans
Google Developer Expert
Google Developer Expert
International Speaker
Spoken at 92 events in 25 countries
Blogger
Blogger
Community Leader
900
1.5K
Trainer
Master of Ceremonies
Master of Ceremonies
NGXS
Overview
-
Angular Developer Friendly
- Decorators
- Dependency Injection
- Life cycle hooks
- Minimal Setup
- Extensible via plugins
- Community driven
Components
Actions
State
Mutate
Dispatch
Notify
Observables
actions
states
A
A
A
S
S
S
Components
Actions
State
Mutate
Dispatch
Notify
Plugins
Actions
State
Counter
Increment
Decrement
Reset
Total
Components
Actions
State
Mutate
Dispatch
Notify
import { State } from '@ngxs/store';
@State<number>({
name: 'counter',
defaults: 0
})
export class CounterState { }
src/app.component.ts
Counter State
app/store/counter.state.ts
Components
Actions
State
Mutate
Dispatch
Notify
export class Increment {
static readonly type = "[Counter] Increment";
}
// const action = new Increment();
// {
// type: "[Counter] Increment"
// }
src/app.component.ts
Increment Action
app/store/counter.actions.ts
import { State, Action, StateContext } from '@ngxs/store';
import { Increment } from '../store/counter.actions';
@State<number>({ name: 'counter', defaults: 0 })
export class CounterState {
@Action(Increment)
Increment(store: StateContext<number>) {
const counter = store.getState();
store.setState(counter+1);
}
}
src/app.component.ts
Increment Action
app/store/counter.state.ts
export class Reset {
static readonly type = "[Counter] Reset";
constructor(public payload: { value: number }) { }
}
// const action = new Reset({ value: 0 });
// {
// type: "[Counter] Reset",
// payload: {
// value: 0
// }
// }
src/app.component.ts
Reset Action
app/store/counter.actions.ts
import { State, Action, StateContext } from '@ngxs/store';
import { Reset } from '../store/counter.actions';
@State<number>({ name: 'counter', defaults: 0 })
export class CounterState {
@Action(Reset)
Reset(store: StateContext<number>, action: Reset) {
const counter = store.getState();
store.setState(action.payload.value);
}
}
src/app.component.ts
Reset Action
app/store/counter.state.ts
Components
Actions
State
Mutate
Dispatch
Notify
@Component({
template: `
<app-counter
(increment)="increment()" (decrement)="decrement()"
(reset)="reset()">
</app-counter>`
})
export class AppComponent {
constructor(private store: Store) { }
increment = () => this.store.dispatch(new Increment());
decrement = () => this.store.dispatch(new Decrement());
reset = () => this.store.dispatch(new Reset({ value: 0 }));
}
src/app.component.ts
Dispatching Actions
app/app.component.ts
@Component({
template: `
<app-counter [total]="counter|async"></app-counter>`
})
export class AppComponent {
@Select(state => state.counter) counter: Observable<number>;
}
src/app.component.ts
Subscribing to the Store
app/app.component.ts
@Component({
selector: 'app-counter',
template: `
<div class="total">{{total}}</div>
<button (click)="increment.emit()">+</button>
<button (click)="decrement.emit()">-</button>
<button (click)="reset.emit()">C</button>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CounterComponent {
@Input() total: number;
@Output() increment = new EventEmitter();
...
}
src/app.component.ts
Counter
app/counter/counter.component.ts
Plugins
Logger
DevTools
Storage
Forms
WebSockets
Router
import { NgxsModule } from '@ngxs/store';
import { NgxsReduxDevtoolsPluginModule }
from '@ngxs/devtools-plugin';
@NgModule({
imports: [
NgxsModule.forRoot([]),
NgxsReduxDevtoolsPluginModule.forRoot()
]
})
export class AppModule {}
src/app.component.ts
Adding DevTools Plugin
import { NgxsModule } from '@ngxs/store';
import { NgxsFormPluginModule }
from '@ngxs/form-plugin';
@NgModule({
imports: [
NgxsModule.forRoot([]),
NgxsFormPluginModule.forRoot(),
NgxsReduxDevtoolsPluginModule.forRoot()
]
})
export class AppModule {}
src/app.component.ts
Adding Forms Plugin
@State({
name: "app",
defaults: {
form: {
model: undefined,
dirty: false,
status: "",
errors: {}
}
}
})
export class AppState {}
src/app.component.ts
Adding initial form state
@Component({
selector: 'form',
template: `
<form ngxsForm="app.form" novalidate
[formGroup]="form" (ngSubmit)="onSubmit()">
<input type="text" formControlName="email" />
<button type="submit">Subscribe</button>
</form>`
})
export class FormComponent { }
src/app.component.ts
Tracking form changes
import { NgxsModule } from '@ngxs/store';
import { NgxsRouterPluginModule }
from '@ngxs/router-plugin';
@NgModule({
imports: [
NgxsModule.forRoot([]),
NgxsRouterPluginModule.forRoot()
]
})
export class AppModule {}
src/app.component.ts
Adding Router Plugin
Advanced NGXS
Todo App
Selectors
{
todos: [{ id: 1, text: 'todo 1', complete: false }],
currentFilter: "SHOW_ALL"
}
// Filtered Todos
[{ id: 1, text: 'todo 1', complete: false }]
src/app.component.ts
Example: Todos State Selector
{
todos: [{ id: 1, text: 'todo 1', complete: false }],
currentFilter: "SHOW_COMPLETED"
}
// Filtered Todos
[]
import { FilterState } from '../filter/filter.state';
@State<Todo[]>({ name: 'todos', defaults: [] })
export class TodosState {
@Selector([FilterState]) static todos(state, currentFilter) {
let t = state.slice().reverse();
switch (currentFilter) {
case 'SHOW_ACTIVE': return t.filter(t => !t.completed);
case 'SHOW_COMPLETED': return t.filter(t => t.completed);
case 'SHOW_ALL':
default: return t;
};
}
}
src/app.component.ts
Selector: Filtered Todos
Comparison
- v3.1.3 (3 months)
- 53 contributors
- Plugins
- 1K stars
- v6.0.1 (2 years)
- 119 contributors
- CLI integration
- 3K stars
More
Austin McDaniel
ngxs.io
Medium
Thanks
NGXS: Redux implemented in 2018
By Gerard Sans
NGXS: Redux implemented in 2018
In this talk, we are going to look into NGXS, a new State Management library released earlier this year. We will introduce its main concepts and cover all the good stuff up to v3.1 release: state/actions life cycle, advanced selectors, snapshots, plugins and more!
- 3,301