布莱恩F爱
从俄勒冈州波特兰的一位专注yobet英雄联盟于Angular、Web技术和Node.js的谷歌开发专家那里学习。
广告 ·ultimatecourses.com
用终极课程学习角度正确的方式

Angular + NgRx:重构模块

用NgRx重构现有应用程序的模块来实现Redux模式。

系列

这篇文章是关于使用NgRX的系列文章的一部分,NgRX是受redux启发的JavaScript的可预测状态容器:

  1. NgRX:基础知识
  2. NgRX:开始
  3. NgRX:重构模块(这个帖子)

示例项目

在这篇文章中,我将与我在新年前夜写的示例申请2017年。我花了大约6个小时的时间制定了使用的英雄应用程序:

  • 角5.0.2
  • RxJS 5.5.2
  • TypeScript 2.4.2

我还使用了Angular的Material UI和Flex Layout模块。你可以下载源代码,然后在GitHub上执行或分叉该存储库:

请注意,如果您拨打存储库,我将在工作中ngrx-refactor-1分支。

示例项目使用json-server.提供我们的客户端角度应用程序可以消耗的简单休息API。

要安装和启动服务器:

美元cd美元的服务器安装美元服务

服务器现在可以在:http://localhost:3000

我们有两种资源:英雄和力量

我们还提供静态文件服务器/静态目录中。示例应用程序使用存储在其中的一些映像服务器/静态/ img

安装并启动客户端:

美元cd客户美元安装$ ng服务

现在,导航到http://localhost:4200你应该看到:

英雄指南应用

以前

以前,我们创建了一个根StateModule然后导入appmodule.。你可以通读下面的文章开始使用NgRx

这篇文章将继续重构的下一个步骤PowersModule使用由NgRx实现的Redux模式。

超级大国

我们的应用程序被建模为英雄教程的角度之旅。我们有两个懒惰的模块:

  1. 英雄模块
  2. PowersModule

heroes模块管理应用程序中的超级英雄,powers模块管理可以分配给超级英雄的能力——所有超级英雄都需要有超级能力!好吧,也许钢铁侠不会,因为托尼·斯塔克没有任何超能力。他只是一个“天才、亿万富翁、花花公子、慈善家”。

我们当前的应用程序已经安装和配置了NgRx,但是我们还没有在应用程序中实现Redux模式。第一步是为我们创造行动和减法权力模型。然后,我们将连接使用PowerService.。最后,我们将重新推荐我们现有的应用程序来使用商店,调度操作并使用选择器函数从存储区检索数据。

如果你是Redux模式的新手,我建议你阅读我的文章Redux和Ngrx的基础知识与角度

行动

让我们深入研究应用程序的操作实现。首先,让我们创建从REST API加载能力数组的操作。

创建一个新文件src / app /州/国家/行动/ powers.ts:

进口{行动}“@ngrx /商店”;进口{权力}"../../../ 核心/模型/ power.model”;进口{createActionType}“. . / . . /共享/跑龙套”;出口常量LOAD_POWERS=createActionType(“LOAD_POWERS”);出口常量LOAD_POWERS_SUCCESS=createActionType('load_powers_success');出口LoadPowers实现了行动{只读的类型=LOAD_POWERS;}出口LoadPowersSuccess实现了行动{只读的类型=LOAD_POWERS_SUCCESS;构造函数(公共有效载荷:权力(]){}}出口类型PowersAction=LoadPowers|LoadPowersSuccess;

让我们快速回顾一下我们的初始操作:

  • 首先,我们进口行动接口。此界面要求我们的动作对象具有类型财产。虽然不需要将操作定义为类,而且我们可以通过简单地提供操作对象来将操作分派到存储中,但是使用类提供了类型安全的额外好处。
  • 接下来,我们导入权力模型。
  • 然后,导入createActionType函数。这对您的应用程序来说不是必需的,但是这是有帮助的,因为它可以确保您的操作类型字符串是独一无二的。如果重复使用同一个字符串两次,此函数将抛出运行时异常,帮助您避免由于两个操作使用相同的字符串值而导致的一些奇怪行为。
  • 然后我们定义两个字符串常量:LOAD_POWERSLOAD_POWERS_SUCCESS
  • 接下来,我们定义LoadPowers类。这个类具有只读类型申请人所要求的财产行动接口。我们设置了价值类型到字符串常量LOAD_POWERS
  • 接下来,我们定义LoadPowersSuccess类。同样,我们指定readonly类型属性,它被设置为字符串常量的值。此外,我们的类有一个构造函数()函数,该函数定义了一个public有效载荷属性,其类型定义是权力对象。
  • 最后,我们定义了一个新的PowersAction类型,它是aLoadPowers对象或A.LoadPowersSuccess对象。我们使用TypeScript联合操作符来定义类型。我们会用到这个PowersAction在我们的减速机()功能的类型安全上行动减速器的参数。

虽然这足以满足我们的应用程序从REST API加载功能的需求,但我们还需要实现以下操作:

  • 增加力量
  • 删除的权力
  • 加载单个电源
  • 选择一个权力
  • 更新一个权力

我已经为我们创建了所有这些动作:

进口{行动}“@ngrx /商店”;进口{权力}"../../../ 核心/模型/ power.model”;进口{createActionType}“. . / . . /共享/跑龙套”;出口常量ADD_POWER=createActionType('add_power');出口常量ADD_POWER_SUCCESS=createActionType(“ADD_POWER_SUCCESS”);出口常量DELETE_POWER=createActionType(“DELETE_POWER”);出口常量DELETE_POWER_SUCCESS=createActionType(“DELETE_POWER_SUCCESS”);出口常量LOAD_POWERS=createActionType(“LOAD_POWERS”);出口常量LOAD_POWERS_SUCCESS=createActionType('load_powers_success');出口常量LOAD_POWER=createActionType(“LOAD_POWER”);出口常量LOAD_POWER_SUCCESS=createActionType(“LOAD_POWER_SUCCESS”)出口常量选择_Power.=createActionType('select_power');出口常量update_power.=createActionType(“UPDATE_POWER”);出口常量update_power_success.=createActionType(“UPDATE_POWER_SUCCESS”);出口AddPower实现了行动{只读的类型=ADD_POWER;构造函数(公共有效载荷:权力){}}出口AddPowerSuccess实现了行动{只读的类型=ADD_POWER_SUCCESS;构造函数(公共有效载荷:权力){}}出口DeletePower实现了行动{只读的类型=DELETE_POWER;构造函数(公共有效载荷:权力){}}出口DeletePowerSuccess实现了行动{只读的类型=DELETE_POWER_SUCCESS;构造函数(公共有效载荷:权力){}}出口LoadPowers实现了行动{只读的类型=LOAD_POWERS;}出口LoadPowersSuccess实现了行动{只读的类型=LOAD_POWERS_SUCCESS;构造函数(公共有效载荷:权力(]){}}出口LoadPower实现了行动{只读的类型=LOAD_POWER;构造函数(公共有效载荷:{ID:数字}){}}出口LoadPowerSuccess实现了行动{只读的类型=LOAD_POWER_SUCCESS;构造函数(公共有效载荷:权力){}}出口SelectPower实现了行动{只读的类型=选择_Power.;构造函数(公共有效载荷:{ID:数字}){}}出口UpdatePower实现了行动{只读的类型=update_power.;构造函数(公共有效载荷:权力){}}出口UpdatePowerSuccess实现了行动{只读的类型=update_power_success.;构造函数(公共有效载荷:权力){}}出口类型PowersAction=AddPower|AddPowerSuccess|DeletePower|DeletePowerSuccess|LoadPowers|LoadPowersSuccess|LoadPower|LoadPowerSuccess|SelectPower|UpdatePower|UpdatePowerSuccess;

减速机

在我们的应用程序中实现Redux模式的下一步是定义减速机()函数。如果你还记得我的帖子在Angular中使用NgRx学习Redux的基础知识,我们的reducer函数是一个纯粹的函数,它的唯一职责是根据所分派的操作改变应用程序的状态。

创建一个新文件src / app /州/国家/还原剂/ powers.ts:

进口{createEntityAdapter,EntityAdapter,EntityState.}“@ngrx /实体”;进口{权力}'../../../ 核心/模型/ power.model”;进口{ADD_POWER_SUCCESS,DELETE_POWER_SUCCESS,LOAD_POWER_SUCCESS,LOAD_POWERS_SUCCESS,PowersAction,选择_Power.,update_power_success.}“. . /行动/权力”;出口接口状态扩展EntityState.<权力>{selectedPowerId:数字;}出口常量适配器:EntityAdapter<权力>=createEntityAdapter();常量Initimstate.:状态=适配器getInitialState({selectedPowerId:空值});出口功能减速机(状态:状态=Initimstate.,行动:PowersAction){开关(行动类型){情况下ADD_POWER_SUCCESS:返回适配器adjone.(行动有效载荷,状态);情况下DELETE_POWER_SUCCESS:返回适配器removeOne(行动有效载荷ID,状态);情况下LOAD_POWER_SUCCESS:返回适配器adjone.(行动有效载荷,状态);情况下LOAD_POWERS_SUCCESS:返回适配器addmany.(行动有效载荷,状态);情况下选择_Power.:返回{状态,selectedPowerId:行动有效载荷ID};情况下update_power_success.:返回适配器updateOne({ID:行动有效载荷ID,变化:行动有效载荷},状态);默认的:返回状态;}}出口常量getSelectedPowerId=(状态:状态)= >状态selectedPowerId;

在我们挖掘上面的代码之前,重要的是要提及我们使用NGRX团队最近发布的新的@ ngrx /实体模块。该模块为我们提供了一个简单的界面,可在商店中有效地存储实体。

该模块还提供了一些方法来方便地更新存储中的实体:

  • addOne ()
  • addMany ()
  • addAll ()
  • removeOne ()
  • removeMany ()
  • removeAll ()
  • updateOne ()
  • updatemany()

好的,让我们回顾一下上面的代码:

  • 首先,从@ ngrx /实体模块导入必要的类和函数。然后我们进口权力模型,以及我们之前定义的操作。
  • 接下来,我们定义状态接口扩展EntityState.接口,指定权力模型作为我们州将包含的实体类型的通用。
  • 接下来,使用createEntityAdapter ()函数,我们定义适配器
  • 然后,定义Initimstate.的商店。我们指定初始态selectedPowerId空值
  • 我们现在准备定义了减速机()函数。
  • 类的默认值状态参数是Initimstate.对象。
  • 还请注意,我们指定行动类型为PowersAction。这将在访问操作的有效负载属性时提供类型安全。
  • 我们打开类型字符串属性中的行动
  • 对于每个操作,我们调用相应的适配器方法以使我们应用的状态变异。
  • 请注意,我们必须手动更新状态对象的select_state.由于适配器上没有内置方法。在这种情况下,我们使用扩展运算符(领先的三个点)来制作状态对象的副本,然后我们覆盖了值的值selectedStateId财产。
  • 还请注意,我们有一个默认情况,即返回所提供的状态,对应用程序的状态不做任何更改。不要忘记这样做,否则,您将在您的应用程序中看到一些奇怪的行为,可能是指示状态为的异常未定义的
  • 最后,我们导出agetSelectedPowerId ()接受状态并返回的函数selectedPowerId属性值。

当我们定义我们的减速机()函数,我们仍然需要把它写到根结点AppState。在我们开始连接我们的减速机()函数,让我们尝试得到一个清晰的图片,我们的商店看起来像:

英雄指南应用

正如你在上图中所看到的,我们的应用程序的状态类似于一个树状结构:

  • /存储(AppState)
  • /存储/权力(PowerState)
  • /商店/权力/权力(州)
  • /store/powers/powers/ids (id字符串或数字数组)
  • /商店/权力/权力/实体(电力实体词典)
  • /store/powers/powers/seletedPowerId(选择的电源id号)

这种具有根状态然后具有特征状态的想法称为分形状态管理。根据NGRX文档:

Store使用分形状态管理,它通过特性模块提供状态组合,无论是主动加载还是延迟加载。

好,现在我们来创建PowersState接口,将有单个权力属性,这将是一个具有的对象id,实体selectedPowerId特性。

创建一个新文件src / app /州/国家/还原剂/ index.ts:

进口{CreateFeaturesElector,createSelector}“@ngrx /存储”;进口{AppState}'../../app.interfaces';进口*作为fromPowers“/权力。”;出口接口PowersState{权力:fromPowers状态;}出口接口状态扩展AppState{权力:PowersState;}出口常量异径接头={权力:fromPowers减速机};出口常量getPowersState=CreateFeaturesElector<PowersState>“权力”;出口常量getPowersEntityState=createSelector(getPowersState,状态= >状态权力);出口常量{selectAll:getAllPowers,selectEntities:刺子,选择性:getPowerIds,selectTotal:getPowersTotal}=fromPowers适配器getSelectors(getPowersEntityState);出口常量getSelectedPowerId=createSelector(getPowersEntityState,fromPowersgetSelectedPowerId);出口常量getSelectedPower=createSelector(刺子,getSelectedPowerId,(实体,selectedPowerId)= >selectedPowerId& &实体(selectedPowerId]);

让我们回顾一下:

  • 首先,我们从NgRx @ngrx/store模块中导入必要的函数。
  • 然后,我们导入我们的根AppState接口。
  • 然后,导入所有导出的值src / app /州/国家/还原剂/ powers.ts我们刚刚创建的文件。
  • 接下来,我们定义一个newPowersState接口。我们州目前有一个权力对象。随着应用程序的增长,我们可以继续向PowersState这是很有必要的。
  • 然后,我们定义一个new状态扩展根目录的接口AppState接口。
  • 然后定义一个异径接头对象的所有简化程序PowersState
  • 注意,每个对象和接口的属性是权力。这很重要,因为它们必须匹配。
  • 属性创建特性选择器createFeatureSelector ()函数。这将返回/store/powers对象PowersState
  • 然后我们创造一个getPowersEntityState使用createSelector()函数。这将返回/store/powers/powers对象状态。注意,选择器的第一个参数是特性选择器,它的值是结果函数的最后一个参数createSelector()函数。
  • 然后创建四个新的选择器函数:getAllPowers (),getPowerEntities (),getPowerIds ()getPowersTotal ()。这些选择器函数是由实体适配器通过getSelectors ()方法。注意,我们提供了父类getPowersEntityState选择器函数。
  • 另请注意,当我们创建我们提供父选择器函数的选择器函数时,请转回我们的选择器。请注意,我们不会调用这些功能,而是我们提供对功能的引用,并允许NGRX为我们调用这些功能。
  • 然后我们创造一个getSelectedPowerId ()选择器函数获取selectedPowerId数量值。
  • 最后,我们创建一个getSelectedPower ()选择器函数获取所选权力实体。

为了进一步解释实体适配器为我们生成的一些选择器函数:

  • getAllPowers ()返回数组权力基于的实体排序id大批。
  • getPowerEntities ()返回一个字典权力实体。
  • getPowerIds ()对象中的每个实体的唯一标识数值数组实体字典。
  • getPowersTotal ()返回总数权力在商店里的实体。

最后一步是在商店中加入该功能。我们会在这方面做到这一点进口数组的StateModule:

进口*作为fromPowers”。/权力/还原剂的;@ngmodule.({进口:(CommonModule,StoreModule.forRoot(appReducer,{Metareducers.:appMetaReducers}),StoreModule.forFeature(“权力”,fromPowers异径接头),StoreRouterConnectingModuleforRoot(),EffectsModuleforRoot((AppEffects]),!环境生产吗?StoreDevtoolsModule仪器():(]],声明:(]})出口StateModule{//删除代码}

注意我们调用的地方forFeature ()静态方法StoreModule.,将特性名称指定为字符串权力。类的引用减速机功能特性。

如果你编译了Angular应用并将其加载到浏览器中,那么它与应用应该没有区别,并且在Redux Chrome DevTools中,查看、添加或更新功能应该没有动作被分派。您的控制台中也不应该有任何异常。

效果

定义了动作,定义了减速机和选择器函数后,现在就可以添加一些效果了。请记住,效果将响应分派到存储的操作并执行一些副作用,通常是异步副作用,如发出HTTP请求。

创建一个新文件src / app /州/国家/ / powers.ts影响:

进口{可注射的}“@angular /核心”;进口{行动,效果}“@ ngrx /效果”;进口{行动}“@ngrx /商店”;进口{可观测的}“rxjs /可见”;进口{地图,switchMap}“rxjs /运营商”;进口{PowersService}"../../../ 核心/服务/ powers.service”;进口{ADD_POWER,AddPower,AddPowerSuccess,DELETE_POWER,DeletePower,DeletePowerSuccess,LOAD_POWER,LOAD_POWERS,LoadPower,LoadPowers,LoadPowersSuccess,LoadPowerSuccess,update_power.,UpdatePower,UpdatePowerSuccess}“. . /行动/权力”;@可注射的()出口PowersEffects{@效果()addpower.:可观测的<行动>=行动减低<AddPower>(ADD_POWER)管道(地图(行动= >行动有效载荷),switchMap(权力= >powersServicecreatePower(权力)),地图(权力= >新的AddPowerSuccess(权力)));@效果()deletePower:可观测的<行动>=行动减低<DeletePower>(DELETE_POWER)管道(地图(行动= >行动有效载荷),switchMap(权力= >powersServicedeletePower(权力)),地图(权力= >新的DeletePowerSuccess(权力)));@效果()loadpowers.:可观测的<行动>=行动减低<LoadPowers>(LOAD_POWERS)管道(switchMap(()= >powersServiceGetPowers.()),地图(权力= >新的LoadPowersSuccess(权力)));@效果()loadPower:可观测的<行动>=行动减低<LoadPower>(LOAD_POWER)管道(地图(行动= >行动有效载荷),switchMap(有效载荷= >powersServicegetPower(有效载荷ID)),地图(权力= >新的LoadPowerSuccess(权力)));@效果()UpdatePower.:可观测的<行动>=行动减低<UpdatePower>(update_power.)管道(地图(行动= >行动有效载荷),switchMap(权力= >powersServiceUpdatePower.(权力)),地图(权力= >新的UpdatePowerSuccess(权力)));构造函数(私人行动:行动,私人powersService:PowersService){}}

我们已经为我们的权力定义了所有效果:

  • addpower.
  • deletePower
  • loadpowers.
  • loadPower
  • UpdatePower.

让我们快速回顾一下addpower.属性:

  • 首先,我们定义addpower.财产在我们PowersEffects类的返回类型可观测的an行动。我们的所有效果都将执行一个副作用,然后返回一个新动作来分派到商店。
  • 我们使用减低()过滤调度的操作的方法,以便仅针对我们想要执行效果的特定操作执行的效果。在这种情况下,我们只会何时执行效果ADD_POWER行动派。
  • 然后我们使用管()RxJS v5.5.x中引入的方法。此方法接受数量不定的操作符,这些操作符接收链中前一个操作符发出的值。这使我们能够将运营商链在一起。
  • 我们首先使用地图()操作符获取有效载荷物体从动作。在本例中,有效载荷是a权力对象。
  • 然后,我们使用theSwitchMap()操作符,它接收权力对象,我们之前的地图()操作符返回。然后返回可观测的从中返回createPower ()方法PowersService。这是一个可观测的新创造的权力对象。
  • 然后,我们使用the地图()操作符返回newAddPowerSuccess操作对象。

如您所见,大多数效果遵循一个非常相似的模式,即使用PowersService执行异步事件。最后,请注意,我们注入了行动PowersService在类的构造函数()函数。

现在,我们的类可以被添加到StateModule:

@ngmodule.({进口:(CommonModule,StoreModule.forRoot(appReducer,{Metareducers.:appMetaReducers}),StoreModule.forFeature(“权力”,fromPowers异径接头),StoreRouterConnectingModuleforRoot(),EffectsModuleforRoot((AppEffects]),EffectsModuleforFeature((PowersEffects]),!环境生产吗?StoreDevtoolsModule仪器():(]],声明:(]})出口StateModule{//删除代码}

我们现在调用forFeature ()静态方法EffectsModule。方法的参数是一个类数组,我们指定PowersEffects类。还请注意,这必须在调用forotoot()方法EffectsModule

同样,在浏览器中构建和加载我们的应用程序应该不会导致对应用程序的任何更改,而且在查看、添加、删除或更新功能时,我们应该不会看到任何被分派到商店的操作。此外,在控制台中不应该有任何异常。

重构指数

好的。使用为我们的商店定义和配置的操作,效果和减速器我们已准备好开始重构我们的应用程序以使用NGRX。

这是我们目前的IndexComponent类:

出口IndexComponent实现了OnInit{权力:可观测的<数组<权力>>;// TODO:使用store代替service构造函数(私人Matdialog.:Matdialog.,私人powersService:PowersService){}ngOnInit(){// TODO:调度动作加载权力权力=powersServiceGetPowers.();}// TODO:使用存储对话状态// TODO:在异能中添加异能不会产生新值添加(){Matdialog.开放(AddPowerDialogComponent);}删除(权力:权力){// TODO:使用存储调度操作powersServicedeletePower(权力)订阅(()= >权力=powersServiceGetPowers.());}}

正如你所看到的,我已经添加了一些TODO占位符,我们可以通过使用NgRx来改进我们的应用程序。让我们开始重构IndexComponent位于src / app / + /集装箱/索引/ index.component.ts权力:

出口IndexComponent实现了OnInit{权力:可观测的<数组<权力>>;构造函数(私人Matdialog.:Matdialog.,私人商店:商店<PowersState>){}ngOnInit(){权力=商店选择(getAllPowers);商店调度(新的LoadPowers());}添加(){Matdialog.开放(AddPowerComponent);}删除(权力:权力){商店调度(新的DeletePower(权力));}}

以下是我们所做的:

  • 首先,我们不再需要导入PowersService构造函数()函数,我们将使用商店
  • 然后我们进口商店构造函数()函数,指定PowersState接口作为泛型类型。
  • 在里面ngOnInit ()的生命周期方法权力公共财产使用getAllPowers选择器。将选择器函数的引用传递给select ()方法商店。然后我们派遣()LoadPowers行动的商店
  • 当用户删除一个权力时,delete ()从子组件上的输出绑定中调用方法。在里面delete ()我们现在正在发出的方法DeletePower操作到商店,提供权力对象作为有效负载。
  • 之前,我们使用PowersService要删除特定的权力对象。我们还执行了一个额外的HTTP请求,以获取更新后的列表权力对象。这是使用Redux模式的一个好处。当我们移除权力来自商店的对象,所有观察者都将获得从可观察到的新值。

重构编辑

接下来,让我们重构更新/突变的容器组件权力对象。

我们的目前EditComponent类位于src / app / + /集装箱/编辑/ edit.component.ts权力:

出口EditComponent实现了OnInit{权力:可观测的<权力>;//使用store代替service构造函数(私人activatedRoute:ActivatedRoute,私人powersService:PowersService,私人间小吃店:MatSnackBar){}ngOnInit(){// TODO:调度行动负载功率权力=activatedRouteparamMap管道(switchMap(paramMap= >powersServicegetPower(paramMap得到('ID'))));}powerChange(权力:权力){// TODO:调度动作更新电源powersServiceUpdatePower.(权力)订阅(()= >{间小吃店开放(“权力更新”,“成功”,{持续时间:2000});});}}

这是我们新重构的组件:

进口{组件,OnInit}“@angular /核心”;进口{MatSnackBar}“@angular /材料”;进口{ActivatedRoute}“@ Angular / Router”;进口{商店}“@ngrx /商店”;进口{可观测的}“rxjs /可见”;进口{第一个,地图,switchMap,利用}“rxjs /运营商”;进口{权力}"../../../ 核心/模型/ power.model”;进口{LoadPower,SelectPower,UpdatePower}“../../../state/powers/actions/powers”;进口{getPowersTotal,getSelectedPower,PowersState}"../../../ 州/国家/还原剂”;@组件({选择器:“app-edit”,templateUrl:”。/ edit.component.html ',样式堡垒:(”。/ edit.component.scss ']})出口EditComponent实现了OnInit{权力:可观测的<权力>;构造函数(私人activatedRoute:ActivatedRoute,私人间小吃店:MatSnackBar,私人商店:商店<PowersState>){}ngOnInit(){权力=activatedRouteparamMap管道(利用(paramMap= >商店调度(新的SelectPower({ID:数量(paramMap得到('ID'))}))),利用(paramMap= >{haspowersinstore.()订阅(存在= >{如果(!存在){商店调度(新的LoadPower({ID:数量(paramMap得到('ID'))}));}});}),switchMap(()= >商店选择(getSelectedPower)));}haspowersinstore.():可观测的<布尔>{返回商店选择(getPowersTotal)管道(第一个(),地图(总计= >总计>0))}powerChange(权力:权力){商店调度(新的UpdatePower(权力));间小吃店开放(“力量救了”,“成功”,{持续时间:2000});}}

让我们来看看更新后的组件代码:

  • 首先,我们将不再使用PowersService在我们的组件中,所以我们可以从构造函数()函数。
  • 我们需要商店,所以通过它注射构造函数()函数。
  • 在里面ngOnInit ()生命周期方法,我们想要得到选择权力基于的对象ID我们路线中的参数。我们将我们paramMap可观测的activatedRoute实例注入构造函数()函数。首先,我们分派SelectPower要更新的行动selectedPowerId在商店的财产。然后,我们检查一下是否有权力商店中的实体。如果没有,我们将派遣LoadPower加载所选功率的动作。最后,我们将返回selected对象的可观察对象权力对象使用select ()方法商店,提供了getSelectedPower选择器函数。
  • haspowersinstore()返回一个可观测的布尔值,指示是否存在权力存储中的对象。我们使用getPowersTotal选择器函数获取实体的计数。我们使用第()操作符,因为我们只关心从可观察对象中发出的第一个值。然后,我们地图()返回的布尔值的计数。
  • powerChange ()通过输出绑定到子组件的输出调用方法。每当我们对电源名称属性的更改时都会调用此方法。如果您在子组件中查看,您会注意到我们使用debounceTime ()方法防止在每次击键时触发事件。要更新电源,我们只需派遣()UpdatePower行动,提供权力对象作为有效负载。
  • 最后,我们将向用户显示一个零食条。这个小零食有点误导,因为我们展示了无论能量是否成功更新。我们希望将其重构为动作和效果,以便通过HTTP请求调用REST API的效果的成功或失败来处理snackbar的显示和隐藏。

以下是使用NgRx加载和编辑功能的快速快照:

英雄指南应用

重构添加

重构的最后一步是更新功能的添加,以使用NgRx存储。

当前的AddComponentDialogComponent位于src / app / + /组件/ add-power-dialog / add-power-dialog.component.ts权力:

出口AddPowerDialogComponent实现了OnInit{形式:FormGroup;//使用store代替service构造函数(私人formBuilder:FormBuilder,私人Matdialogref.:MatDialogRef<AddPowerDialogComponent>,私人powersService:PowersService){}//删除代码保存(){如果(!形式有效的){返回;}// TODO:调度操作存储powersServicecreatePower(形式价值)订阅(()= >关闭());}}

这是我们新重构的save ()方法:

出口AddPowerDialogComponent{保存(){如果(!形式有效的){返回;}商店调度(新的AddPower(形式价值));关闭();}}

控件中的许多现有代码都被省略了AddPowerDialogComponent简洁。以下是我们所做的:

  • 首先,我们移除PowerService.因为我们不再需要它了。
  • 然后,我们注射了商店通过构造函数()函数。
  • 最后,我们更新save ()方法来分派AddPower行动到商店。

此外,请注意,我们在用户单击保存后立即关闭对话框。我们可能想要重新推荐这使得对话框仅在保存成功的情况下关闭。这是我们可以重新推荐的东西,以便打开和关闭我们的对话框是操作,因此我们有效果将调用对话框的影响open ()close ()方法。

重构完成

我有一个额外的分支项目在GitHub命名ngrx-refactor-2包含所有重构代码。随意下载或叉子:

家庭作业

虽然我们已经重构了添加、编辑、加载和更新功能以使用Redux模式,但是打开和关闭添加功能对话框的状态并没有保存在我们的存储中。您可能会认为这符合应用程序的需要。但是,您可能希望将所有应用程序状态存储在存储中,包括切换打开和关闭对话框的状态以添加新的功能,以及显示和隐藏snackbar。

布莱恩F爱

嗨,我是布莱恩。我对TypeScript, Angular和Node.js感兴趣。我和我最好的朋友邦妮结婚了,我住在波特兰,我经常滑雪。