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并且换样式的话,改动的还是比较大的,就先简单解决这个问题。
发表评论