antd组件库升级之后代码不生效
项目业务之前的代码设计了类似radio单选框取消选择的相关逻辑,用的是下面类似的代码实现的。但近期对ng-zorro-antd组件库升级之后,下面的代码不生效了。
import { Component } from '@angular/core'; @Component({ selector: 'nz-demo-radio-radiogroup', template: ` <nz-radio-group [(ngModel)]="radioValue"> <label nz-radio (click)="click('A')" nzValue="A">A</label> <label nz-radio nzValue="B">B</label> <label nz-radio nzValue="C">C</label> <label nz-radio nzValue="D">D</label> </nz-radio-group> ` }) export class NzDemoRadioRadiogroupComponent { radioValue = 'A'; click(value: any) { if (this.radioValue === value) { this.radioValue = '' } } }
于是我到组件库源码里去找原因,并写demo打断点调试
- 将nz-radio-group绑定的radioValue值清空会首先走group组件下面的逻辑
writeValue(value: NzSafeAny): void { this.value = value; this.nzRadioService.select(value); this.cdr.markForCheck(); }
- nz-radio 和 nz-radio-group都是共用这个NzRadioService,且组件内init时都有对selected$这个流做监听
@Injectable() export class NzRadioService { selected$ = new ReplaySubject<NzSafeAny>(1); touched$ = new Subject<void>(); disabled$ = new ReplaySubject<boolean>(1); name$ = new ReplaySubject<string>(1); touch(): void { this.touched$.next(); } select(value: NzSafeAny): void { this.selected$.next(value); } setDisabled(value: boolean): void { this.disabled$.next(value); } setName(value: string): void { this.name$.next(value); } } // radio.component.ts ====> ngOnInit this.nzRadioService.selected$.pipe(takeUntil(this.destroy$)).subscribe(value => { const isChecked = this.isChecked; this.isChecked = this.nzValue === value; // We don't have to run `onChange()` on each `nz-radio` button whenever the `selected$` emits. // If we have 8 `nz-radio` buttons within the `nz-radio-group` and they're all connected with // `ngModel` or `formControl` then `onChange()` will be called 8 times for each `nz-radio` button. // We prevent this by checking if `isChecked` has been changed or not. if ( this.isNgModel && isChecked !== this.isChecked && // We're only intereted if `isChecked` has been changed to `false` value to emit `false` to the ascendant form, // since we already emit `true` within the `setupClickListener`. this.isChecked === false ) { this.onChange(false); } this.cdr.markForCheck(); });
- 当监听完值改变后,后面又执行了radio的click事件,把点击哪个radio的value值传过去了,所以之前的清空值操作就被覆盖了。
private setupClickListener(): void { this.ngZone.runOutsideAngular(() => { fromEvent<MouseEvent>(this.elementRef.nativeElement, 'click') .pipe(takeUntil(this.destroy$)) .subscribe(event => { /** prevent label click triggered twice. **/ event.stopPropagation(); event.preventDefault(); if (this.nzDisabled || this.isChecked) { return; } this.ngZone.run(() => { // !!! again this.nzRadioService?.select(this.nzValue); if (this.isNgModel) { this.isChecked = true; this.onChange(true); } this.cdr.markForCheck(); }); }); }); }
解决方法
清空值的操作加setTimeout 使组件库内部先执行完click后续再执行。
import { Component } from '@angular/core'; @Component({ selector: 'nz-demo-radio-radiogroup', template: ` <nz-radio-group [(ngModel)]="radioValue"> <label nz-radio (click)="click('A')" nzValue="A">A</label> <label nz-radio nzValue="B">B</label> <label nz-radio nzValue="C">C</label> <label nz-radio nzValue="D">D</label> </nz-radio-group> ` }) export class NzDemoRadioRadiogroupComponent { radioValue = 'A'; click(value: any) { if (this.radioValue === value) { setTimeout(()=>{ this.radioValue = '' }) } } }
总结
其实组件库单选radio本身是不支持取消选择的,正解应该是用checkbox实现相关的业务逻辑才对,但很久之前的业务逻辑涉及到很多地方的修改,此时再换checkbox并且换样式的话,改动的还是比较大的,就先简单解决这个问题。