Angular Material范围滑块:程序化修改值的一致性校验问题
解决Angular Material范围滑块程序化更新的校验问题
当程序化修改Angular Material范围滑块的数值时,组件会在两个数值完成更新前就校验起始值与结束值的一致性,导致更新异常。例如当前滑块范围为10-30(区间0-100),尝试改为80-100时,最终滑块显示30-100——这是因为更新起始值(80)时,结束值仍为30,组件会强制将起始值限制为不超过结束值,从而覆盖了设置的80。
问题复现代码
import { Component } from '@angular/core'; import { bootstrapApplication } from '@angular/platform-browser'; import { MatSliderModule } from '@angular/material/slider'; import { FormsModule } from '@angular/forms'; @Component({ selector: 'app-root', template: ` <div style="margin: 20px;"> <mat-slider min="0" max="100" step="10" style="width: 100%" showTickMarks> <input [(ngModel)]="sliderStart" matSliderStartThumb> <input [(ngModel)]="sliderEnd" matSliderEndThumb> </mat-slider> </div> <div> sliderStart : {{ sliderStart }} <br> sliderEnd : {{ sliderEnd }} <br> </div> <div style="margin-top: 10px; display: flex; gap: 10px;"> <button (click)="reset()">reset</button> <button (click)="changeTo2050()">change to 20-50</button> <button (click)="changeTo80100()">change to 80-100</button> </div> `, imports: [FormsModule, MatSliderModule], }) export class App { sliderStart = 10; sliderEnd = 30; reset() { this.sliderStart = 10; this.sliderEnd = 30; } changeTo2050() { this.sliderStart = 20; this.sliderEnd = 50; } changeTo80100() { this.sliderEnd = 100; this.sliderStart = 80; } } bootstrapApplication(App);
可行解决方案
方案1:使用NgZone批量更新值
利用NgZone.run()确保Angular在所有数值更新完成后再触发变更检测和组件校验,避免中间状态的校验干扰。
import { Component, NgZone } from '@angular/core'; import { bootstrapApplication } from '@angular/platform-browser'; import { MatSliderModule } from '@angular/material/slider'; import { FormsModule } from '@angular/forms'; @Component({ selector: 'app-root', template: ` <div style="margin: 20px;"> <mat-slider min="0" max="100" step="10" style="width: 100%" showTickMarks> <input [(ngModel)]="sliderStart" matSliderStartThumb> <input [(ngModel)]="sliderEnd" matSliderEndThumb> </mat-slider> </div> <div> sliderStart : {{ sliderStart }} <br> sliderEnd : {{ sliderEnd }} <br> </div> <div style="margin-top: 10px; display: flex; gap: 10px;"> <button (click)="reset()">reset</button> <button (click)="changeTo2050()">change to 20-50</button> <button (click)="changeTo80100()">change to 80-100</button> </div> `, imports: [FormsModule, MatSliderModule], }) export class App { sliderStart = 10; sliderEnd = 30; constructor(private zone: NgZone) {} reset() { this.zone.run(() => { this.sliderStart = 10; this.sliderEnd = 30; }); } changeTo2050() { this.zone.run(() => { this.sliderStart = 20; this.sliderEnd = 50; }); } changeTo80100() { this.zone.run(() => { this.sliderEnd = 100; this.sliderStart = 80; }); } } bootstrapApplication(App);
方案2:使用响应式表单FormGroup(推荐)
响应式表单的setValue()方法可以批量设置控件值,不会触发中间状态的校验,从根源避免问题。
import { Component } from '@angular/core'; import { bootstrapApplication } from '@angular/platform-browser'; import { MatSliderModule } from '@angular/material/slider'; import { ReactiveFormsModule, FormGroup, FormBuilder } from '@angular/forms'; @Component({ selector: 'app-root', template: ` <div style="margin: 20px;"> <mat-slider min="0" max="100" step="10" style="width: 100%" showTickMarks> <input formControlName="start" matSliderStartThumb> <input formControlName="end" matSliderEndThumb> </mat-slider> </div> <div> sliderStart : {{ sliderForm.get('start')?.value }} <br> sliderEnd : {{ sliderForm.get('end')?.value }} <br> </div> <div style="margin-top: 10px; display: flex; gap: 10px;"> <button (click)="reset()">reset</button> <button (click)="changeTo2050()">change to 20-50</button> <button (click)="changeTo80100()">change to 80-100</button> </div> `, imports: [ReactiveFormsModule, MatSliderModule], }) export class App { sliderForm: FormGroup; constructor(private fb: FormBuilder) { this.sliderForm = this.fb.group({ start: 10, end: 30 }); } reset() { this.sliderForm.setValue({ start: 10, end: 30 }); } changeTo2050() { this.sliderForm.setValue({ start: 20, end: 50 }); } changeTo80100() { this.sliderForm.setValue({ start: 80, end: 100 }); } } bootstrapApplication(App);
内容的提问来源于stack exchange,提问作者Olivier MM
相关产品推荐
相关产品推荐

