Angular中切换行内Type列值后,如何刷新Input/Output列验证状态?
我开发了一个基于Angular Material的动态表格Angular应用,表格包含Type、InputValue、OutputValue三列。通过(selectionChange)和[(ngModel)]实现了选择行内Type列的值后,自动更新同一行的InputValue和OutputValue列。
相关代码如下:
HTML代码
<ng-container matColumnDef="type"> <th mat-header-cell *matHeaderCellDef mat-sort-header > Type </th> <td mat-cell *matCellDef="let row"> <div *ngIf="selection.isSelected(row); else display"> <mat-form-field class="table"> <mat-select [(ngModel)]="row.type" placeholder="Select..." name="type" #type="ngModel" (selectionChange)="typeChange(row)" required> <mat-option *ngFor="let type of types" [value]="aType">{{aType}}</mat-option> </mat-select> <mat-error *ngIf="type.errors"> <strong>Required</strong> </mat-error> </mat-form-field> </div> <ng-template #display>{{row.type}}</ng-template> </td> </ng-container> <ng-container matColumnDef="inputValue" > <th mat-header-cell *matHeaderCellDef mat-sort-header >Input Value </th> <td mat-cell *matCellDef="let row"> <div *ngIf="selection.isSelected(row); else display"> <mat-form-field class="table"> <input matInput [(ngModel)]="row.inputValue" name="inputValue" type="number" #inputValue="ngModel" [placeholder]="placeholder" rangeValidator [minValue]="minValue" maxValue="8190" required> <mat-error *ngIf="inputValue.errors"> <strong>{{utils.getIntegerRangeError(inputValue.errors)}}</strong> </mat-error> </mat-form-field> </div> <ng-template #display>{{row.inputValue}}</ng-template> </td> </ng-container> <ng-container matColumnDef="outputValue" > <th mat-header-cell *matHeaderCellDef mat-sort-header > Output Value </th> <td mat-cell *matCellDef="let row"> <div *ngIf="selection.isSelected(row); else display"> <mat-form-field class="table"> <input matInput [(ngModel)]="row.outputValue" name="outputValue" type="number" #outputValue="ngModel" [placeholder]="placeholder" rangeValidator [minValue]="minValue" maxValue="8190" required> <mat-error *ngIf="outputValue.errors"> <strong>{{utils.getIntegerRangeError(outputValue.errors)}}</strong> </mat-error> </mat-form-field> </div> <ng-template #display>{{row.outputValue}}</ng-template> </td> </ng-container> <div class="button-bar"> <button mat-raised-button (click)="onSave()" [disabled]="templateForm.invalid || isInputPidZero()" color="primary">Save</button> <button mat-raised-button (click)="onCancel(templateForm)" cdkFocusInitial class="button-rounded cancel-action">Cancel</button> </div>
TS代码
typeChange(element : Type) { switch (element.type) { case 'A': element.inputValue = 2; element.outputValue = 2; element.minValue = 2; element.placeholder = "2 - 100"; break; case 'B': element.inputValue = 1; element.outputValue = 1; element.minValue = 1; element.placeholder = "1 - 100"; break; } } isInputPidZero(): boolean { return this.dataSource.data.some(row => (row.inputValue < row.minValue || row.outputValue < row.minValue || row.inputValue === undefined || row.outputValue === undefined)); }
我还根据Type的选择为InputValue和OutputValue列设置了验证规则:选择Type为A时,两列的minValue为2;选择Type为B时,minValue为1,若输入值小于minValue则显示错误提示。
现在遇到的问题是:输入值1后选择Type为A,会正常显示错误提示;但随后切换Type为B时,值1对B来说是合法的,可错误提示仍未消失。需要修改代码,在切换Type时刷新这两列的验证状态。
问题根源是Angular表单控件不会自动重新验证依赖的外部参数(比如这里的minValue),必须手动触发验证更新,具体步骤如下:
1. 修正HTML控件的绑定关系
首先把InputValue和OutputValue列的placeholder、minValue绑定到当前行数据,确保参数变化能被控件感知,同时为控件添加明确的模板标识:
<!-- InputValue列修改 --> <input matInput [(ngModel)]="row.inputValue" name="inputValue" type="number" #inputValueCtrl="ngModel" [placeholder]="row.placeholder" rangeValidator [minValue]="row.minValue" maxValue="8190" required [attr.data-row-id]="row.id"> <!-- OutputValue列修改 --> <input matInput [(ngModel)]="row.outputValue" name="outputValue" type="number" #outputValueCtrl="ngModel" [placeholder]="row.placeholder" rangeValidator [minValue]="row.minValue" maxValue="8190" required [attr.data-row-id]="row.id">
注:row.id是行数据的唯一标识,若你的行数据没有id,可以替换为其他唯一字段。
2. 在TS中手动触发控件验证
引入ViewChildren获取所有表单控件引用,在typeChange方法中找到当前行的输入控件并强制更新验证状态:
import { Component, ViewChildren, QueryList } from '@angular/core'; import { NgModel } from '@angular/forms'; // ... 其他组件代码 @ViewChildren(NgModel) ngModels!: QueryList<NgModel>; typeChange(element: Type) { switch (element.type) { case 'A': element.inputValue = 2; element.outputValue = 2; element.minValue = 2; element.placeholder = "2 - 100"; break; case 'B': element.inputValue = 1; element.outputValue = 1; element.minValue = 1; element.placeholder = "1 - 100"; break; } // 触发当前行输入控件的重新验证 this.triggerRowValidation(element); } private triggerRowValidation(row: Type) { this.ngModels.forEach(model => { const element = model.element.nativeElement; const rowId = element.getAttribute('data-row-id'); // 匹配当前行的InputValue和OutputValue控件 if (rowId === row.id.toString() && (model.name === 'inputValue' || model.name === 'outputValue')) { // 标记为已触碰并更新验证状态 model.control.markAsTouched(); model.control.updateValueAndValidity({ emitEvent: false }); } }); }
3. 验证全局状态同步
isInputPidZero方法会自动随row.minValue的变化重新计算,按钮的disabled状态会同步更新,无需额外修改。
内容的提问来源于stack exchange,提问作者Learner

