Angular 2 Component Communication - Talk by Rob McDiarmid
-
Upload
amrita-chopra -
Category
Technology
-
view
318 -
download
0
Transcript of Angular 2 Component Communication - Talk by Rob McDiarmid
Component Communication
What is a component
@Component({ selector: 'myComp', template: `<div> Content: {{myVar}} </div>`})class MyComponent { myVar: string;
constructor() { this.myVar = 'hello'; }}
Output
Input<html> <myComp></myComp></html>
<html> <myComp> <div> Content: hello </div> </myComp></html>
Parent → Child
@Component({ selector: 'parent', template: `<div> Parent content <child [param]="myVar"></child> Parent content</div>`})class ParentComponent { myVar = 'hello';}
@Component({ selector: 'child', template: '<div>Child: {{param}}</div>'})class ChildComponent { @Input() param: string;}
<html> <parent> <div> Parent content <child> Child hello </child> Parent content </div> </parent></html>
Output
Input<html> <parent></parent></html>
wizardComp
@Input() Demo
Child → Parent
@Component({ selector: 'parent', template: `<div> <child (childEvent)="handelChildEvent($event)"></child></div>`})class ParentComponent { handelChildEvent(message) { console.log(message); }}
@Component({ selector: 'child', template: ` <button (click)="childEvent.emit('clicked')">Click me</button>`})class ChildComponent { @Output() childEvent = new EventEmitter();}
@Output() Demo
Sibling → Sibling
@Component({ selector: 'sibling2', template: `<button (click)="myService.increment()"> Increment</button>`})class Sibling2Component { constructor(public myService: MyService) {
}}
@Component({ selector: 'sibling1', template: `{{myService.counter}}`})class Sibling1Component { constructor(public myService: MyService) {
}}
class MyService { counter: number = 0;
increment() { this.counter++; }}
Sibling Demo
user.service.tsexport class UserService { users: User[] = [];}
user.service.tsexport class UserService { private users$ = new BehaviorSubject([]);}
user.service.tsexport class UserService { private users$ = new BehaviorSubject([]);
addUser(user) { let users = [user, ...this.users$.getValue()]; this.users$.next(users); }}
user.service.tsexport class UserService { private users$ = new BehaviorSubject([]);
addUser(user) { let users = [user, ...this.users$.getValue()]; this.users$.next(users); }
removeUser(user) { let users = this.users$.getValue().filter(u => u !== user); this.users$.next(users); }}
user.service.tsexport class UserService { private users$ = new BehaviorSubject([]);
addUser(user) { let users = [user, ...this.users$.getValue()]; this.users$.next(users); }
removeUser(user) { let users = this.users$.getValue().filter(u => u !== user); this.users$.next(users); }
getUsers() { return this.users$.asObservable(); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of filteredUsers"> <input #firstName (keyup)="filterUsers(firstName.value)"></div>`})export class UserSearchComponent { filteredUsers: User[];
constructor(public userService: UserService) { }
filterUsers(search) { this.filteredUsers = this.userService.users.filter(user => user.firstName.includes(search)); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of filteredUsers"> <input #firstName (keyup)="filterUsers(firstName.value)"></div>`})export class UserSearchComponent { users: Observable<User[]>;
constructor(public userService: UserService) { this.users = userService.getUsers(); }
filterUsers(search) { this.filteredUsers = this.userService.users.filter(user => user.firstName.includes(search)); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of filteredUsers"> <input #firstName (keyup)="filterUsers(firstName.value)"></div>`})export class UserSearchComponent { users: Observable<User[]>;
constructor(public userService: UserService) { this.users = userService.getUsers(); }
filterUsers(search) { this.filteredUsers = this.userService.users.filter(user => user.firstName.includes(search)); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> <input #firstName (keyup)="filterUsers(firstName.value)"></div>`})export class UserSearchComponent { users: Observable<User[]>;
constructor(public userService: UserService) { this.users = userService.getUsers(); }
filterUsers(search) { this.filteredUsers = this.userService.users.filter(user => user.firstName.includes(search)); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> <input #firstName (keyup)="filterUsers(firstName.value)"></div>`})export class UserSearchComponent { users: Observable<User[]>;
constructor(public userService: UserService) { this.users = userService.getUsers(); }
filterUsers(search) { this.filteredUsers = this.userService.users.filter(user => user.firstName.includes(search)); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> <input #firstName (keyup)="firstNameSearch.next(firstName.value)"></div>`})export class UserSearchComponent { users: Observable<User[]>; firstNameSearch = new BehaviorSubject('');
constructor(public userService: UserService) { this.users = userService.getUsers(); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> <input #firstName (keyup)="firstNameSearch.next(firstName.value)"></div>`})export class UserSearchComponent { users: Observable<User[]>; firstNameSearch = new BehaviorSubject('');
constructor(public userService: UserService) { this.users = userService.getUsers(); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> <input #firstName (keyup)="firstNameSearch.next(firstName.value)"></div>`})export class UserSearchComponent { users: Observable<User[]>; firstNameSearch = new BehaviorSubject('');
constructor(public userService: UserService) { this.users = Observable.combineLatest( userService.getUsers(), this.firstNameSearch, (users, search) => { return users.filter(user => user.firstName.includes(search)); } ); }}
http://rxmarbles.com/#combineLatest
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> ... </div>`})export class UserSearchComponent { users: Observable<User[]>; firstNameSearch = new BehaviorSubject('');
constructor(public userService: UserService) { this.users = Observable.combineLatest( userService.getUsers(), this.firstNameSearch, (users, search) => { ... } ); }
removeUser(user: User) { this.userService.users = this.userService.users.filter(u => u !== user); this.filterUsers(this.firstName.nativeElement.value); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> ... </div>`})export class UserSearchComponent { users: Observable<User[]>; firstNameSearch = new BehaviorSubject('');
constructor(public userService: UserService) { this.users = Observable.combineLatest( userService.getUsers(), this.firstNameSearch, (users, search) => { ... } ); }
removeUser(user: User) { this.userService.removeUser(user); }}
List Search
User Service
users$ = BehaviorSubject([ 'User1', 'User2', 'User3'])
List Search
User Service
users$ = BehaviorSubject([ 'User1', 'User2', 'User3'])
[ 'User1', 'User2', 'User3']
List Search
User1 ✖User2 ✖User3 ✖
User Service
users$ = BehaviorSubject([ 'User1', 'User2', 'User3'])
[ 'User1', 'User2', 'User3']
User1User2User3
List Search
User1 ✖User2 ✖User3 ✖
User1User2User3
User Service
users$ = BehaviorSubject([ 'User1', 'User2', 'User3'])
removeUser('User2')
List Search
User1 ✖User2 ✖User3 ✖
User1User2User3
User Service
users$ = BehaviorSubject([ 'User1', 'User3'])
removeUser('User2')
List Search
User1 ✖User2 ✖User3 ✖
User1User2User3
User Service
users$ = BehaviorSubject([ 'User1', 'User3'])
[ 'User1', 'User3']
removeUser('User2')
List Search
User1 ✖User3 ✖
User1User3
User Service
users$ = BehaviorSubject([ 'User1', 'User3'])
[ 'User1', 'User3']
removeUser('User2')
user.service.tsexport class UserService { private users$ = new BehaviorSubject([]);
addUser(user) { let users = [user, ...this.users$.getValue()]; this.users$.next(users); }
removeUser(user) { let users = this.users$.getValue().filter(u => u !== user); this.users$.next(users); }
getUsers() { return this.users$.asObservable(); }};
user.service.tsexport const userReducer = (users = [], action) => { /* addUser(user) { let users = [user, ...this.users$.getValue()]; this.users$.next(users); }
removeUser(user) { let users = this.users$.getValue().filter(u => u !== user); this.users$.next(users); }
getUsers() { return this.users$.asObservable(); } */};
user.service.tsexport const userReducer = (users = [], action) => { switch (action.type) { /* addUser(user) { let users = [user, ...this.users$.getValue()]; this.users$.next(users); }
removeUser(user) { let users = this.users$.getValue().filter(u => u !== user); this.users$.next(users); }
getUsers() { return this.users$.asObservable(); } */ }};
user.service.tsexport const userReducer = (users = [], action) => { switch (action.type) { case 'CREATE_USER': /* let users = [user, ...this.users$.getValue()]; this.users$.next(users); */ case 'DELETE_USER': /* let users = this.users$.getValue().filter(u => u !== user); this.users$.next(users); */ default: /* return this.users$.asObservable(); */ }};
user.service.tsexport const userReducer = (users = [], action) => { switch (action.type) { case 'CREATE_USER': return [...users, User.fromMockData()];
case 'DELETE_USER': /* let users = this.users$.getValue().filter(u => u !== user); this.users$.next(users); */ default: /* return this.users$.asObservable(); */ }};
user.service.tsexport const userReducer = (users = [], action) => { switch (action.type) { case 'CREATE_USER': return [...users, User.fromMockData()];
case 'DELETE_USER': return users.filter(user => user.id !== action.payload.id);
default: /* return this.users$.asObservable(); */ }};
user.service.tsexport const userReducer = (users = [], action) => { switch (action.type) { case 'CREATE_USER': return [...users, User.fromMockData()];
case 'DELETE_USER': return users.filter(user => user.id !== action.payload.id);
default: return users; }};
app.module.tsimport {userReducer} from "./reducers/userReducer";
@NgModule({ imports: [StoreModule.provideStore({ users: userReducer }) ], declarations: [/*...*/], providers: [/*...*/], bootstrap: [/*...*/]})class AppModule { }
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> ... </div>`})export class UserSearchComponent { users: Observable<User[]>; firstNameSearch = new BehaviorSubject('');
constructor(public userService: UserService) { this.users = Observable.combineLatest( userService.getUsers(), this.firstNameSearch, (users, search) => { ... } ); }
removeUser(user: User) { this.userService.removeUser(user); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> ... </div>`})export class UserSearchComponent { users: Observable<User[]>; firstNameSearch = new BehaviorSubject('');
constructor(public userService: UserService) { this.users = Observable.combineLatest( userService.getUsers(), this.firstNameSearch, (users, search) => { ... } ); }
removeUser(user: User) { this.userService.removeUser(user); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> ... </div>`})export class UserSearchComponent { users: Observable<User[]>; firstNameSearch = new BehaviorSubject('');
constructor(public userService: UserService) { this.users = Observable.combineLatest( userService.getUsers(), this.firstNameSearch, (users, search) => { ... } ); }
removeUser(user: User) { this.userService.removeUser(user); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> ... </div>`})export class UserSearchComponent { users: Observable<User[]>; firstNameSearch = new BehaviorSubject('');
constructor(public store: Store<AppState>) { this.users = Observable.combineLatest( userService.getUsers(), this.firstNameSearch, (users, search) => { ... } ); }
removeUser(user: User) { this.userService.removeUser(user); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> ... </div>`})export class UserSearchComponent { users: Observable<User[]>; firstNameSearch = new BehaviorSubject('');
constructor(public store: Store<AppState>) { this.users = Observable.combineLatest( userService.getUsers(), this.firstNameSearch, (users, search) => { ... } ); }
removeUser(user: User) { this.userService.removeUser(user); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> ... </div>`})export class UserSearchComponent { users: Observable<User[]>; firstNameSearch = new BehaviorSubject('');
constructor(public store: Store<AppState>) { this.users = Observable.combineLatest( store.select(s => s.users), this.firstNameSearch, (users, search) => { ... } ); }
removeUser(user: User) { this.userService.removeUser(user); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> ... </div>`})export class UserSearchComponent { users: Observable<User[]>; firstNameSearch = new BehaviorSubject('');
constructor(public store: Store<AppState>) { this.users = Observable.combineLatest( store.select(s => s.users), this.firstNameSearch, (users, search) => { ... } ); }
removeUser(user: User) { this.userService.removeUser(user); }}
userSearch.component.ts@Component({ selector: 'user-search', template: `<div *ngFor="let user of users | async"> ... </div>`})export class UserSearchComponent { users: Observable<User[]>; firstNameSearch = new BehaviorSubject('');
constructor(public store: Store<AppState>) { this.users = Observable.combineLatest( store.select(s => s.users), this.firstNameSearch, (users, search) => { ... } ); }
removeUser(user: User) { this.store.dispatch({type: 'DELETE_USER', payload: {id: user.id}}); }}
Takeaways
- Everything is a component!!!
Takeaways - Parent → Child
- Use an @Input() binding on child component
- Use es6 setters or ngOnChanges() to handle changes
- When @Input() doesn’t work, inject a @ViewChild()
Takeaways - Child → Parent
- Use an @Output() binding on child component
- Pass events to parent through an EventEmitter()@Output() doThing = new EventEmitter();doThing.emit('some event');
- In the parent, get the payload of the event with $event<child (doThing)="handelThing($event)"></child>
- If you can’t use an @Output() binding you can inject the parent component directly into the child
Takeaways - Sibling → Sibling
- Use a service to communicate between siblings
- Try to avoid sharing mutable state- Use observables to push events out
from a service to componentsprivate state$ = new BehaviorSubject<>({});doSomething(thing) { this.state$.next(thing);}
Takeaways - ngrx/store (Redux)
- ngrx/store library brings redux like approach to Angular 2
- Centralized state- One way data flow
Further ReadingAngular CookbookComponent Interaction
rxjs/storegithub.com/ngrx/store
egghead.ioBuilding Angular 2 Components
egghead.ioStep-by-Step Async JavaScript with RxJS
Rob McDiarmidSlides: tinyurl.com/ng2-components
@robianmcd