forkJoin 控制多个请求并行 在angular项目中使用rxjs的observable来控制异步请求很方便很舒服,但是有些时候得考虑一些特殊的问题,比如有两个请求相互依赖的情况,希望在所有请求都响应后再采取行动,如何处理?
以前使用Promise
的时候,有个promise.all
的方法,可以控制所有请求请求完成后执行操作,相同的,rxjs
也提供了forkJoin
操作符来控制请求的并行。
forkJoin
操作符接受一组observable
作为参数,当所有的observable
完成时,将每个observable
的最新值作为数组发出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import {forkJoin} from 'rxjs' ;const preApi = forkJoin ([ this .commonApi .getMerchantNameList (false ), this .tokenApi .getChannelMethod ({id : this .data .id }), ]); preApi.subscribe (([merchantListRes, channelListRes] ) => { });
响应式表单嵌套检测问题 angular使用响应式表单,字段检测变得非常清晰和可配置话,使用起来很爽,但是最近遇到一个问题,比如我一个表单中有一个多选选项,多选选项上面有一个全选的checkbox,我在选择上面的全选或全不选的时候需要更新下面的多选列表,在点击下面的多选列表的时候也要更新上面的全选checkbox,这种情况怎么办?第一反应是直接来绑定formContrllor:
1 2 3 4 this .validateForm = this .fb .group ({ checkList : [[], Validators .required ], allChecked : [false ], });
我们checkList
就是我们多选选项,allChecked
就是全选按钮,我们要分别跟踪对应的触发事件,所以我们直接来订阅:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 this .selectAllSub = this .validateForm .controls ['allChecked' ].valueChanges .subscribe (value => { this .indeterminate = false ; if (value) { this .validateForm .controls ['checkList' ].patchValue (this .validateForm .controls ['checkList' ].value .map (item => { return { value : item.value , label : item.label , checked : true , }; }), { emitEvent : false , }); } else { this .validateForm .controls ['checkList' ].patchValue (this .validateForm .controls ['checkList' ].value .map (item => { return { value : item.value , label : item.label , checked : false , }; })); } }); this .merchantListSub = this .validateForm .controls ['checkList' ].valueChanges .subscribe (data => { if (data.every (item => item.checked === false )) { this .validateForm .controls ['allChecked' ].patchValue (false ); this .indeterminate = false ; } else if (data.every (item => item.checked === true )) { this .validateForm .controls ['allChecked' ].patchValue (true ); this .indeterminate = false ; } else { this .indeterminate = true ; } });
第一个监听的是全选选择框的值,如果全选了,就将checkList全设置为true,如果全不选就将checkList为false。第二个监听的是checkList的值,如果全选中了就将全选选择框选中,如果全不选中就将全选选择框设置为未选中,其中indenterminate
是介于全选和不选的中间状态,即选择了部分。
逻辑很清晰,也可以跑起来。但是我们操作后发现,选择一下之后,就说maxsize
了,也就是堆栈溢出。。。。
why?
来分析下,我们两个监听,A和B,监听A当A发生变化时改动B,监听B当B发生变动时改动A,好像陷入死循环了啊???
那我们得找一个方法,间听A当A发生变动时改动B,但这时B的监听不需要作出动作。还能有这种方法???
我们这里用了formBuild的formController,而监听发生在赋值的过程之后,那么赋值的地方是不是有什么文章?
formController的赋值操作有两个方法:
setValue(value, options) 当值发生变化时,该配置项决定如何传播变更以及发出事件。
pathValue()
在 FormControl 这个层次上,该函数的功能和 setValue 完全相同。 但 FormGroup 和 FormArray 上的 patchValue则具有不同的行为。
那么来看setValue的options:
onlySelf?: boolean; 如果为true,则每次变更只影响该控件本身,不影响父控件,默认为false
emitEvent?: boolean; 当控件值发生变化时,statusChanges和valueChanges这两个Observable都会以最近的状态和值发出事件,如果为false,则不会发出事件。默认为true。
emitModelToViewChange?: boolean; 如果为ture,则每次变化都会触发一个onChange事件以更新试图。默认为true
emitViewToModelChange?: boolean; 如果为true,则每次变化都会触发一个ngModelChange事件以更新模型。默认为true。
ok,我们需要注意emitEvent
这个配置,如果这个为false,那么是不是可以达到我们想要的效果?修改下我们前面的监听函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 this .merchantListSub = this .validateForm .controls ['merchantList' ].valueChanges .subscribe (data => { if (data.every (item => item.checked === false )) { this .validateForm .controls ['allChecked' ].patchValue (false , { emitEvent : false , }); this .indeterminate = false ; } else if (data.every (item => item.checked === true )) { this .validateForm .controls ['allChecked' ].patchValue (true , { emitEvent : false , }); this .indeterminate = false ; } else { this .indeterminate = true ; } });
非常nice,可以实现我们的效果。
但是,在提交表单的时候,发现提交的时候会把修改的值清理掉,竟然丢失了。。。。看看我们的保存方法里面涉及到表单的操作,发现有这块代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let isValid = true ; for (const i of Object .keys (this .validateForm .controls )) { if (i === 'allChecked' ) { continue ; } this .validateForm .controls [i].markAsDirty (); if (this .validateForm .controls [i].status === 'INVALID' ) { isValid = false ; } this .validateForm .controls [i].updateValueAndValidity (); } if (!isValid) { return resolve (); }
看看这里用到的方法:
updateValueAndValidity 重新计算控件的值和校验状态
markAsDirty 把控件标记为dirty,当控件通过ui修改过时控件会变成dirty的。
也就是,我们在js中修改的值并没有被承认,也就是没有标记为dirty,我们需要添加这个语句:
1 this .validateForm .controls ['allChecked' ].markAsDirty ();
ok完美😊
最终代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 this .merchantListSub = this .validateForm .controls ['merchantList' ].valueChanges .subscribe (data => { if (data.every (item => item.checked === false )) { this .validateForm .controls ['allChecked' ].patchValue (false , { emitEvent : false , }); this .indeterminate = false ; } else if (data.every (item => item.checked === true )) { this .validateForm .controls ['allChecked' ].patchValue (true , { emitEvent : false , }); this .indeterminate = false ; } else { this .indeterminate = true ; } this .validateForm .controls ['allChecked' ].markAsDirty (); });