ngrx 对组件(与路由关联)init 的调度操作

ngrx dispatching action on component (associated with a route) init

angular 5+, ngrx 4+

我想做的是在初始化 ArticlesListComponent 时触发 ARTICLES_LIST_REQUEST,与 path /articles 关联。

有很多方法可以做到这一点,例如在 ngOnInit 中使用 dispatch,在守卫上使用 dispatch,但我想做的是使用 @ngrx/router-store, listen to ROUTER_NAVIGATION and map it to ARTICLES_LIST_REQUEST (actually ARTICLES_LIST_PARAMS_SET, to save current route params to the store, but it don't matter), 现在有一对的问题。

因为效果,即使是那些在 forFeature 注册的效果,在全球范围内都是 运行ning,如果我要设置这个效果来监听 ROUTER_NAVIGATION 在多个模块(文章,人物,品牌等),它会 运行 多次,一次触发所有操作,所以我需要一种方法来仅针对我感兴趣的路线过滤它..但是如何?由于 angular 路由器和导航操作中没有命名路由 returns 整个树的快照,因此无法知道刚刚激活的特定路由。我现在正在做的是通过 url 检查和过滤,例如它是否以 /articles 开头以及下一个字符是否不是 / (过滤掉 /articles/<id>。但我真的不喜欢它,我真的觉得它不能安全地涵盖所有情况。

曾经有一种方法可以在组件内暂停/启动/取消订阅/订阅效果,我认为这不再可能了,但它会将责任移回组件,这不会比从组件/守卫调度动作要好得多。

我不应该为此使用 router-store 吗?

这是我最终得到的结果:

1) 添加唯一的 name 到路由定义的 data

export const ROUTES: Routes = [
  {
    path: '/articles',
    component: ArticlesListPage,
    data: {
      name: 'articles-list'
    }
  }
];

2) 为 router-store 创建自定义 serializer,仅提取 paramsqueryParamsname(来自 data.name)对于树

中的每个 child
function createSerializedTree(node: ActivatedRouteSnapshot) {
  const tree = {
    queryParams: node.queryParams,
    params: node.params,
    name: node.data.name,
    children: node.children.map(createSerializedTree),
    firstChild: undefined
  };

  tree.firstChild = tree.children[0];

  return tree;
}

3) 为 let 创建一个函数,过滤并映射到名称与您提供的名称匹配的第一条路由

const _checkIsNodeHasName = (name: string, node: SerializedRouterNode) => {
  if (node.name === name) {
    return node;
  } else if (node.firstChild) {
    return _checkIsNodeHasName(name, node.firstChild);
  } else {
    return undefined;
  }
}

export const filterAndMapRoute = (name: string) => {
  return (navigationAction$: Observable<RouterNavigationAction<RouterStateUrl>>) => {
    return navigationAction$
      .map(action => action.payload.routerState)
      .filter((state) => !!_checkIsNodeHasName(name, state.root))
      .map((state) => _checkIsNodeHasName(name, state.root));
  };
};

现在我可以做到了

@Injectable()
export class ArticlesEffects {
  @Effect()
  private _onRouteChange$ = this._action$
    .ofType(ROUTER_NAVIGATION)
    .let(filterAndMapRoute('posts-list'))
    .map(/* ... */) // map to whatever action I need
}

它或多或少只是一个概念证明,实际上我不仅需要检查 firstChild,还需要检查所有 children。

我首先只使用了树的最后一个 firstChild,但在某些情况下这是行不通的,例如,如果您想从这些路线中保存 id[=29] =]

/articles/:id/graph / /articles/:id/table,最后一个 firstChild,不会包含 id,因此需要找到匹配的第一个 child名称并映射到该名称(如果找到 none 则进行过滤)。