Page 中通过构造函数注入 Store,基于 Store 进行数据操作。
注意 Component 使用了 changeDetection: ChangeDetectionStrategy.OnPush.
OnPush
means that the change detector's mode will be set to CheckOnce
during hydration.
/app/containers/collection-page.ts
import 'rxjs/add/operator/let'; import { Component, ChangeDetectionStrategy } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; import * as fromRoot from '../reducers'; import { Book } from '../models/book'; @Component({ selector: 'bc-collection-page', changeDetection: ChangeDetectionStrategy.OnPush, template: `My Collection `, /** * Container components are permitted to have just enough styles * to bring the view together. If the number of styles grow, * consider breaking them out into presentational * components. */ styles: [` md-card-title { display: flex; justify-content: center; } `] }) export class CollectionPageComponent { books$: Observable ; constructor(store: Store ) { this.books$ = store.select(fromRoot.getBookCollection); } }
/app/containers/find-book-page.ts
import 'rxjs/add/operator/let'; import 'rxjs/add/operator/take'; import { Component, ChangeDetectionStrategy } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; import * as fromRoot from '../reducers'; import * as book from '../actions/book'; import { Book } from '../models/book'; @Component({ selector: 'bc-find-book-page', changeDetection: ChangeDetectionStrategy.OnPush, template: `` }) export class FindBookPageComponent { searchQuery$: Observable ; books$: Observable ; loading$: Observable<boolean>; constructor(private store: Store ) { this.searchQuery$ = store.select(fromRoot.getSearchQuery).take(1); this.books$ = store.select(fromRoot.getSearchResults); this.loading$ = store.select(fromRoot.getSearchLoading); } search(query: string) { this.store.dispatch(new book.SearchAction(query)); } }
注意,点击搜索之后,我们回派发一个 Search 的 Action,但是,在 Book 的 Reducer 中并不处理这个 Action, @ngrx/effect 将会监控这个 Action,进行异步处理。
/app/containers/not-found-page.ts
import { Component, ChangeDetectionStrategy } from '@angular/core'; @Component({ selector: 'bc-not-found-page', changeDetection: ChangeDetectionStrategy.OnPush, template: ``, styles: [` :host { text-align: center; } `] }) export class NotFoundPageComponent { } 404: Not Found Hey! It looks like this page doesn't exist yet.
通过 @Input() 参数将数据从页面传递给下面的 Component,事件从底层 Component 冒泡上来。
/app/containers/selected-book-page.ts
import { Component, ChangeDetectionStrategy } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; import * as fromRoot from '../reducers'; import * as collection from '../actions/collection'; import { Book } from '../models/book'; @Component({ selector: 'bc-selected-book-page', changeDetection: ChangeDetectionStrategy.OnPush, template: `detail [book] ="book$ | async" [inCollection]="isSelectedBookInCollection$ | async" (add)="addToCollection($event)" (remove)="removeFromCollection($event)"> ` }) export class SelectedBookPageComponent { book$: Observable; isSelectedBookInCollection$: Observable<boolean>; constructor(private store: Store ) { this.book$ = store.select(fromRoot.getSelectedBook); this.isSelectedBookInCollection$ = store.select(fromRoot.isSelectedBookInCollection); } addToCollection(book: Book) { this.store.dispatch(new collection.AddBookAction(book)); } removeFromCollection(book: Book) { this.store.dispatch(new collection.RemoveBookAction(book)); } }
/app/containers/view-book-page.ts
import '@ngrx/core/add/operator/select'; import 'rxjs/add/operator/map'; import { Component, OnDestroy, ChangeDetectionStrategy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Store } from '@ngrx/store'; import { Subscription } from 'rxjs/Subscription'; import * as fromRoot from '../reducers'; import * as book from '../actions/book'; /** * Note: Container components are also reusable. Whether or not * a component is a presentation component or a container * component is an implementation detail. * * The View Book Page's responsibility is to map router params * to a 'Select' book action. Actually showing the selected * book remains a responsibility of the * SelectedBookPageComponent */ @Component({ selector: 'bc-view-book-page', changeDetection: ChangeDetectionStrategy.OnPush, template: `` }) export class ViewBookPageComponent implements OnDestroy { actionsSubscription: Subscription; constructor(store: Store , route: ActivatedRoute) { this.actionsSubscription = route.params .select ('id') .map(id => new book.SelectAction(id)) .subscribe(store); } ngOnDestroy() { this.actionsSubscription.unsubscribe(); } }
/app/containers/app.ts
import 'rxjs/add/operator/let'; import { Observable } from 'rxjs/Observable'; import { Component, ChangeDetectionStrategy } from '@angular/core'; import { Store } from '@ngrx/store'; import * as fromRoot from '../reducers'; import * as layout from '../actions/layout'; @Component({ selector: 'bc-app', changeDetection: ChangeDetectionStrategy.OnPush, template: `` }) export class AppComponent { showSidenav$: Observable<boolean>; constructor(private store: Store [open]="showSidenav$ | async"> My Collection Browse Books Book Collection ) { /** * Selectors can be applied with the `select` operator which passes the state * tree to the provided selector */ this.showSidenav$ = this.store.select(fromRoot.getShowSidenav); } closeSidenav() { /** * All state updates are handled through dispatched actions in 'container' * components. This provides a clear, reproducible history of state * updates and user interaction through the life of our * application. */ this.store.dispatch(new layout.CloseSidenavAction()); } openSidenav() { this.store.dispatch(new layout.OpenSidenavAction()); } }