我们从 Action 名称开始。
在 ngRx 中,不同的 Action 需要一个 Action Type 进行区分,一般来说,这个 Action Type 是一个字符串,如何定义和使用这个字符串是需要首先考虑的问题。需要保证不同的 Action 名称不能冲突,使用的时候还需要方便,编码的时候,最好有提示等等。
首先处理命名冲突问题,示例使用 util 中定义的一个字典来检查是否已经定义了一个 Action
/** * This function coerces a string into a string literal type. * Using tagged union types in TypeScript 2.0, this enables * powerful typechecking of our reducers. * * Since every action label passes through this function it * is a good place to ensure all of our action labels * are unique. */ const typeCache: { [label: string]: boolean } = {}; export function type<T>(label: T | ‘‘): T { if (typeCache[<string>label]) { throw new Error(`Action type "${label}" is not unique"`); } typeCache[<string>label] = true; return <T>label; }
使用 TypeScript 的 Playground 翻译一下,可以得到如下的结果:
var typeCache = {}; function type(label) { if (typeCache[label]) { throw new Error("Action type \"" + label + "\" is not unique\""); } typeCache[label] = true; return label; }
可以更加直观地看到,这个 type 函数可以接收一个字符串,在类型缓存对象 typeCache 中检查是否已经设置过这个 Key, 如果已经设置过一次,抛出异常,这样可以避免命名冲突问题。如果没有,则将这个 Action Type 保存为缓存对象的键,值设置为 true. 最后返回这个 Action Type 的字符串。
具体的 Action Type 有哪些呢?它们分别定义在 /app/actions/books.ts,/app/actions/layouts.ts 和 /app/actions/collection.ts 中。
我们先看看 /app/actions/books.ts,先从最后的 5 行开始看起。
这里使用了 TypeScript 中的 String Literal Types, 使得我们可以利用强类型的方式来使用字符串。
/** * Export a type alias of all actions in this action group * so that reducers can easily compose action types */ export type Actions = SearchAction | SearchCompleteAction | LoadAction | SelectAction;
然后,我们再看看 Action 接口的定义,它来自 @ngrx/store.
export interface Action {
  readonly type: string;
}
这里是对一个 Action 基本的要求,需要一个字符串类型的 Action 名称。
为了便于使用每一种 Action ,这里定义了每种 Action 所对应的 Action 实现。例如 SearchAction 的实现如下所示:
/** * Every action is comprised of at least a type and an optional * payload. Expressing actions as classes enables powerful * type checking in reducer functions. * * See Discriminated Unions: https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions */ export class SearchAction implements Action { type = ActionTypes.SEARCH; constructor(public payload: string) { } }
可以看到它的 Action 类型使用了 String Literal Type 定义,值固定设置为了 "SearchAction",可以通过构造函数传递这个 Action 所使用的附加数据。
其它三种的 Action 以此类推。
然后就是类型名称重复问题了,通过调用前面定义的 type 函数,可以检查是否已经定义过特定的 Action Type.
/** * For each action type in an action group, make a simple * enum object for all of this group‘s action types. * * The ‘type‘ utility function coerces strings into string * literal types and runs a simple check to guarantee all * action types in the application are unique. */ export const ActionTypes = { SEARCH: type(‘[Book] Search‘), SEARCH_COMPLETE: type(‘[Book] Search Complete‘), LOAD: type(‘[Book] Load‘), SELECT: type(‘[Book] Select‘), };
代码的全部内容如下所示。
import { Action } from ‘@ngrx/store‘;
import { Book } from ‘../models/book‘;
import { type } from ‘../util‘;
/**
 * For each action type in an action group, make a simple
 * enum object for all of this group‘s action types.
 *
 * The ‘type‘ utility function coerces strings into string
 * literal types and runs a simple check to guarantee all
 * action types in the application are unique.
 */
export const ActionTypes = {
  SEARCH:           type(‘[Book] Search‘),
  SEARCH_COMPLETE:  type(‘[Book] Search Complete‘),
  LOAD:             type(‘[Book] Load‘),
  SELECT:           type(‘[Book] Select‘),
};
/**
 * Every action is comprised of at least a type and an optional
 * payload. Expressing actions as classes enables powerful
 * type checking in reducer functions.
 *
 * See Discriminated Unions: https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions
 */
export class SearchAction implements Action {
  type = ActionTypes.SEARCH;
  constructor(public payload: string) { }
}
export class SearchCompleteAction implements Action {
  type = ActionTypes.SEARCH_COMPLETE;
  constructor(public payload: Book[]) { }
}
export class LoadAction implements Action {
  type = ActionTypes.LOAD;
  constructor(public payload: Book) { }
}
export class SelectAction implements Action {
  type = ActionTypes.SELECT;
  constructor(public payload: string) { }
}
/**
 * Export a type alias of all actions in this action group
 * so that reducers can easily compose action types
 */
export type Actions
  = SearchAction
  | SearchCompleteAction
  | LoadAction
  | SelectAction;
import { Action } from ‘@ngrx/store‘;
import { type } from ‘../util‘;
export const ActionTypes = {
  OPEN_SIDENAV:   type(‘[Layout] Open Sidenav‘),
  CLOSE_SIDENAV:  type(‘[Layout] Close Sidenav‘)
};
export class OpenSidenavAction implements Action {
  type = ActionTypes.OPEN_SIDENAV;
}
export class CloseSidenavAction implements Action {
  type = ActionTypes.CLOSE_SIDENAV;
}
export type Actions
  = OpenSidenavAction
  | CloseSidenavAction;
以及 /app/actions/collection.ts 的定义。
import { Action } from ‘@ngrx/store‘;
import { Book } from ‘../models/book‘;
import { type } from ‘../util‘;
export const ActionTypes = {
  ADD_BOOK:             type(‘[Collection] Add Book‘),
  ADD_BOOK_SUCCESS:     type(‘[Collection] Add Book Success‘),
  ADD_BOOK_FAIL:        type(‘[Collection] Add Book Fail‘),
  REMOVE_BOOK:          type(‘[Collection] Remove Book‘),
  REMOVE_BOOK_SUCCESS:  type(‘[Collection] Remove Book Success‘),
  REMOVE_BOOK_FAIL:     type(‘[Collection] Remove Book Fail‘),
  LOAD:                 type(‘[Collection] Load‘),
  LOAD_SUCCESS:         type(‘[Collection] Load Success‘),
  LOAD_FAIL:            type(‘[Collection] Load Fail‘),
};
/**
 * Add Book to Collection Actions
 */
export class AddBookAction implements Action {
  type = ActionTypes.ADD_BOOK;
  constructor(public payload: Book) { }
}
export class AddBookSuccessAction implements Action {
  type = ActionTypes.ADD_BOOK_SUCCESS;
  constructor(public payload: Book) { }
}
export class AddBookFailAction implements Action {
  type = ActionTypes.ADD_BOOK_FAIL;
  constructor(public payload: Book) { }
}
/**
 * Remove Book from Collection Actions
 */
export class RemoveBookAction implements Action {
  type = ActionTypes.REMOVE_BOOK;
  constructor(public payload: Book) { }
}
export class RemoveBookSuccessAction implements Action {
  type = ActionTypes.REMOVE_BOOK_SUCCESS;
  constructor(public payload: Book) { }
}
export class RemoveBookFailAction implements Action {
  type = ActionTypes.REMOVE_BOOK_FAIL;
  constructor(public payload: Book) { }
}
/**
 * Load Collection Actions
 */
export class LoadAction implements Action {
  type = ActionTypes.LOAD;
  constructor() { }
}
export class LoadSuccessAction implements Action {
  type = ActionTypes.LOAD_SUCCESS;
  constructor(public payload: Book[]) { }
}
export class LoadFailAction implements Action {
  type = ActionTypes.LOAD_FAIL;
  constructor(public payload: any) { }
}
export type Actions
  = AddBookAction
  | AddBookSuccessAction
  | AddBookFailAction
  | RemoveBookAction
  | RemoveBookSuccessAction
  | RemoveBookFailAction
  | LoadAction
  | LoadSuccessAction
  | LoadFailAction;
原文:http://www.cnblogs.com/haogj/p/6537064.html