@ngrx/effect

前面我们提到,在 Book 的 reducer 中,并没有 Search 这个 Action 的处理,由于它需要发出一个异步的请求,等到请求返回前端,我们需要根据返回的结果来操作 store。所以,真正操作 store 的应该是 Search_Complete 这个 Action。我们在 recducer 已经看到了。

对于 Search 来说,我们需要见到这个 Action 就发出一个异步的请求,等到异步处理完毕,根据返回的结果,构造一个 Search_Complete 来将处理的结果派发给 store 进行处理。

这个解耦是通过 @ngrx/effect 来处理的。

@ngrx/effect 提供了装饰器 @Effect 和 Actions 来帮助我们检查 store 派发出来的 Action,将特定类型的 Action 过滤出来进行处理。监听特定的 Action, 当发现特定的 Action 发出之后,自动执行某些操作,然后将处理的结果重新发送回 store 中。

Book 搜索处理

以 Book 为例,我们需要监控 Search 这个 Action, 见到这个 Action 就发出异步的请求,然后接收请求返回的数据,如果在接收完成之前,又遇到了下一个请求,那么,直接结束上一个请求的返回数据。如何找到下一个请求呢?使用 skip 来获取下一个 Search Action。

源码

/src/effects/book.ts

import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/skip';
import 'rxjs/add/operator/takeUntil';
import { Injectable } from '@angular/core';
import { Effect, Actions, toPayload } from '@ngrx/effects';
import { Action } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { empty } from 'rxjs/observable/empty';
import { of } from 'rxjs/observable/of'; import { GoogleBooksService } from '../services/google-books';
import * as book from '../actions/book'; /**
* Effects offer a way to isolate and easily test side-effects within your
* application.
* The `toPayload` helper function returns just
* the payload of the currently dispatched action, useful in
* instances where the current state is not necessary.
*
* Documentation on `toPayload` can be found here:
* https://github.com/ngrx/effects/blob/master/docs/api.md#topayload
*
* If you are unfamiliar with the operators being used in these examples, please
* check out the sources below:
*
* Official Docs: http://reactivex.io/rxjs/manual/overview.html#categories-of-operators
* RxJS 5 Operators By Example: https://gist.github.com/btroncone/d6cf141d6f2c00dc6b35
*/ @Injectable()
export class BookEffects { @Effect()
search$: Observable<Action> = this.actions$
.ofType(book.ActionTypes.SEARCH)
.debounceTime(300)
.map(toPayload)
.switchMap(query => {
if (query === '') {
return empty();
} const nextSearch$ = this.actions$.ofType(book.ActionTypes.SEARCH).skip(1); return this.googleBooks.searchBooks(query)
.takeUntil(nextSearch$)
.map(books => new book.SearchCompleteAction(books))
.catch(() => of(new book.SearchCompleteAction([])));
}); constructor(private actions$: Actions, private googleBooks: GoogleBooksService) { }
}

collection.ts

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/toArray';
import { Injectable } from '@angular/core';
import { Action } from '@ngrx/store';
import { Effect, Actions } from '@ngrx/effects';
import { Database } from '@ngrx/db';
import { Observable } from 'rxjs/Observable';
import { defer } from 'rxjs/observable/defer';
import { of } from 'rxjs/observable/of'; import * as collection from '../actions/collection';
import { Book } from '../models/book'; @Injectable()
export class CollectionEffects { /**
* This effect does not yield any actions back to the store. Set
* `dispatch` to false to hint to @ngrx/effects that it should
* ignore any elements of this effect stream.
*
* The `defer` observable accepts an observable factory function
* that is called when the observable is subscribed to.
* Wrapping the database open call in `defer` makes
* effect easier to test.
*/
@Effect({ dispatch: false })
openDB$: Observable<any> = defer(() => {
return this.db.open('books_app');
}); /**
* This effect makes use of the `startWith` operator to trigger
* the effect immediately on startup.
*/
@Effect()
loadCollection$: Observable<Action> = this.actions$
.ofType(collection.ActionTypes.LOAD)
.startWith(new collection.LoadAction())
.switchMap(() =>
this.db.query('books')
.toArray()
.map((books: Book[]) => new collection.LoadSuccessAction(books))
.catch(error => of(new collection.LoadFailAction(error)))
); @Effect()
addBookToCollection$: Observable<Action> = this.actions$
.ofType(collection.ActionTypes.ADD_BOOK)
.map((action: collection.AddBookAction) => action.payload)
.mergeMap(book =>
this.db.insert('books', [ book ])
.map(() => new collection.AddBookSuccessAction(book))
.catch(() => of(new collection.AddBookFailAction(book)))
); @Effect()
removeBookFromCollection$: Observable<Action> = this.actions$
.ofType(collection.ActionTypes.REMOVE_BOOK)
.map((action: collection.RemoveBookAction) => action.payload)
.mergeMap(book =>
this.db.executeWrite('books', 'delete', [ book.id ])
.map(() => new collection.RemoveBookSuccessAction(book))
.catch(() => of(new collection.RemoveBookFailAction(book)))
); constructor(private actions$: Actions, private db: Database) { }
}

See also:

@ngrx/effects

skip

takeUntil

What is the purpose of ngrx effects library?

ngRx 官方示例分析 - 6 - Effect的更多相关文章

  1. ngRx 官方示例分析 - 3. reducers

    上一篇:ngRx 官方示例分析 - 2. Action 管理 这里我们讨论 reducer. 如果你注意的话,会看到在不同的 Action 定义文件中,导出的 Action 类型名称都是 Action ...

  2. ngRx 官方示例分析 - 2. Action 管理

    我们从 Action 名称开始. 解决 Action 名称冲突问题 在 ngRx 中,不同的 Action 需要一个 Action Type 进行区分,一般来说,这个 Action Type 是一个字 ...

  3. ngRx 官方示例分析 - 1. 介绍

    ngRx 的官方示例演示了在具体的场景中,如何使用 ngRx 管理应用的状态. 示例介绍 示例允许用户通过查询 google 的 book  API  来查询图书,并保存自己的精选书籍列表. 菜单有两 ...

  4. ngRx 官方示例分析 - 4.pages

    Page 中通过构造函数注入 Store,基于 Store 进行数据操作. 注意 Component 使用了 changeDetection: ChangeDetectionStrategy.OnPu ...

  5. ngRx 官方示例分析 - 5. components

    组件通过标准的 Input 和 Output 进行操作,并不直接访问 store. /app/components/book-authors.ts import { Component, Input ...

  6. RocketMQ源码分析之从官方示例窥探:RocketMQ事务消息实现基本思想

    摘要: RocketMQ源码分析之从官方示例窥探RocketMQ事务消息实现基本思想. 在阅读本文前,若您对RocketMQ技术感兴趣,请加入RocketMQ技术交流群 RocketMQ4.3.0版本 ...

  7. Halcon斑点分析官方示例讲解

    官方示例中有许多很好的例子可以帮助大家理解和学习Halcon,下面举几个经典的斑点分析例子讲解一下 Crystals 图中显示了在高层大气中采集到的晶体样本的图像.任务是分析对象以确定特定形状的频率. ...

  8. DotNetBar for Windows Forms 12.7.0.10_冰河之刃重打包版原创发布-带官方示例程序版

    关于 DotNetBar for Windows Forms 12.7.0.10_冰河之刃重打包版 --------------------11.8.0.8_冰河之刃重打包版------------- ...

  9. DotNetBar for Windows Forms 12.5.0.2_冰河之刃重打包版原创发布-带官方示例程序版

    关于 DotNetBar for Windows Forms 12.5.0.2_冰河之刃重打包版 --------------------11.8.0.8_冰河之刃重打包版-------------- ...

随机推荐

  1. sql存储过程中使用 output

    1.sql存储过程中使用 output CREATE PROCEDURE [dbo].[P_Max] @a int, -- 输入 @b int, -- 输入 @Returnc int output - ...

  2. MySQL迁移方案(后续再补充)

    出处:黑洞中的奇点 的博客 http://www.cnblogs.com/kelvin19840813/ 您的支持是对博主最大的鼓励,感谢您的认真阅读.本文版权归作者所有,欢迎转载,但请保留该声明. ...

  3. IndentationError: unexpected indent

    都知道python是对格式要求很严格的,写了一些python但是也没发现他严格在哪里,今天遇到了IndentationError: unexpected indent错误我才知道他是多么的严格.    ...

  4. Atlas 安装报错 package Atlas-2.2.1-1.x86_64 is intended for a x86_64 architecture

    安装atlas 报错: package Atlas-2.2.1-1.x86_64 is intended for a x86_64 architecture 百度了好久没找到相关信息,最后看见官网文档 ...

  5. 巧用CSS实现宝马LOGO

    某天突然遇到一个有趣的面试题,需用CSS实现一个宝马的Logo,第一反应就是这不是老生常谈的八卦图的小变形吗,只需用伪元素就可轻易的实现啦,但是细看要求说只能在一个标签里写样式,所以呜呜呜...请教下 ...

  6. ionic2 开始第一个App(二)

    安装App指令:ionic start 你的项目文件夹名称 tabs 安装指令如: ionic start myApp tabs 安装时间有点长,耐心等待~ 进入myApp文件夹指令:cd myApp ...

  7. Oracle之 11gR2 RAC 修改监听器端口号的步骤

    Oracle 11gR2 RAC 修改监听器端口号的步骤 说明:192.168.188.181 为public ip1192.168.188.182 为public ip2192.168.188.18 ...

  8. java中重载一定在一个类里面吗?

    虽然这些概念在翻译成中文的过程中,有很多不同的翻译方式但本质上只有两种说法,就是Override和Overload其中,Overload一般都被翻译成重载而Override的翻译就乱七八糟了,所谓覆盖 ...

  9. 为什么用CDN给你网站加速?

    大多数人都知道,一个用户在打开一个新网站的时候,如果网站打开的速度过慢,用户是很难继续浏览的.因而很多网站的运营人员想方设法的提高网站的加载速度.我们也相信速度是一个成功网站的必备要素之一,速度不够快 ...

  10. JAVA中的 static使用

    主要内容: 1.静态变量 2.静态方法 3.静态代码块 静态变量 我们知道,可以基于一个类创建多个该类的对象,每个对象都拥有自己的成员,互相独立.然而在某些时候,我们更希望该类所有的对象共享同一个成员 ...