Angular 2 Notes - Modules
State Management
The Problem With Concurrency
Building components which communicate with each other is a typical task involving state. We frequently have to keep up to date with different Angular components interacting with the same state: when more than one component accesses and modifies that state we call it
shared mutable state
.
- keeping the state consistent
Create a store to manage the state. E.g., the state of Note can be accessed from the store.import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Injectable } from '@angular/core';
import 'rxjs/Rx';
export interface Note {
color: string,
title: string,
value: string,
id?: string | number,
createdAt?: string,
updatedAt?: string,
userId?: string
}
export interface State {
notes: Array<Note>
}
const defaultState : State = {
notes: []
}
const _store = new BehaviorSubject<State>(defaultState);
@Injectable()
export class Store {
private _store = _store;
changes = this._store.asObservable().distinctUntilChanged()
setState(state: State) {
this._store.next(state);
}
getState(): State {
return this._store.value;
}
purge() {
this._store.next(defaultState);
}
}
Apply a store-helper
service to keep state…import { Injectable } from '@angular/core';
import { Store } from '../store';
@Injectable()
export class StoreHelper {
constructor(private store: Store) {}
update(prop, state) {
const currentState = this.store.getState();
this.store.setState(Object.assign({}, currentState, { [prop]: state }));
}
add(prop, state) {
const currentState = this.store.getState();
const collection = currentState[prop];
this.store.setState(Object.assign({}, currentState, { [prop]: [state, ...collection] }));
}
findAndUpdate(prop, state) {
const currentState = this.store.getState();
const collection = currentState[prop];
this.store.setState(Object.assign({}, currentState, {[prop]: collection.map(item => {
if (item.id !== state.id) {
return item;
}
return Object.assign({}, item, state)
})}))
}
findAndDelete(prop, id) {
const currentState = this.store.getState();
const collection = currentState[prop];
this.store.setState(Object.assign({}, currentState, {[prop]: collection.filter(item => item.id !== id)}));
}
}
```
Apply the store...
```js
export class Notes {
notes = [];
constructor(
private store: Store,
private noteService: NoteService
)
{
this.noteService.getNotes().subscribe();
// this.store.changes.pluck('notes')
// .subscribe((notes: any) => this.notes = notes);
this.store.changes
.map(data => data.notes)
.subscribe(notes => this.notes = notes);
}
onCreateNote(note) {
this.noteService.createNote(note)
.subscribe();
// .subscribe(note => this.notes.push(note));
}
onNoteChecked(note) {
this.noteService.completeNote(note)
.subscribe();
// .subscribe(note => {
// const i = this.notes.findIndex(localNote => localNote.id === note.id);
// this.notes.splice(i, 1);
// });
}
}
Authentication Management
Create authentication service, auth.ts
…import { Injectable } from '@angular/core';
import {CanActivate, Router} from '@angular/router';
import 'rxjs/Rx';
@Injectable()
export class AuthService implements CanActivate {
JWT_KEY: string = 'retain_token';
JWT: string = '';
constructor(private router: Router) {}
isAuthorized(): boolean {
return Boolean(this.JWT);
}
canActivate(): boolean {
const canActivate = this.isAuthorized();
this.onCanActivate(canActivate);
return canActivate;
}
onCanActivate(canActivate: boolean) {
if (!canActivate) {
this.router.navigate(['', 'auth']);
}
}
}
Add auth control in routes.ts
.import { RouterModule } from '@angular/router';
import { ModuleWithProviders } from '@angular/core';
import { Main, Notes, About, Auth } from './containers';
import { AuthService } from './services';
export const routes: ModuleWithProviders = RouterModule.forRoot([
{
path: '',
component: Main,
canActivate: [ AuthService ],
children: [
{ path: '', component: Notes },
{ path: 'about', component: About }
]
},
{ path: 'auth', component: Auth},
{ path: '**', redirectTo: '' }
]);
Auth page for example…import { Component } from '@angular/core';
@Component({
selector: 'auth-container',
template: ` <div class="auth-container">
<h1>Auth</h1>
</div>
`
})
export class Auth {}