如何使用 Angular 重置自定义表单控件

Posted

技术标签:

【中文标题】如何使用 Angular 重置自定义表单控件【英文标题】:How can I reset a custom form control with Angular 【发布时间】:2018-11-09 21:54:04 【问题描述】:

我有一个带有验证的自定义表单控件。我在其中使用了一个独立的 FormControl 来处理值和一些验证。

当控件从另一个 FormGroup 重置时,Angular 有没有办法重置内部 FormControl?

Bellow 是我的自定义表单控件。我希望能够重置 durationControl。

duration.component.ts

import  ChangeDetectionStrategy, Component, Input, OnInit, Optional, Self  from '@angular/core';
import  AbstractControl, ControlValueAccessor, FormControl, NgControl, ValidationErrors, Validators  from '@angular/forms';
import  Observable  from 'rxjs';
import  map, startWith, tap  from 'rxjs/operators';
import  Regex  from '../../constants';

@Component(
    selector: 'my-duration',
    templateUrl: 'duration.component.html',
    changeDetection: ChangeDetectionStrategy.OnPush
)
export class DurationComponent implements ControlValueAccessor, OnInit 
    @Input() required: boolean;

    durations: string[] = [
        '15m',
        '30m',
        '45m',
        '1h',
        '1h 15m',
        '1h 30m',
        '1h 45m',
        '2h'
    ];
    filteredDurations: Observable<string[]>;

    durationControl = new FormControl('', [
        Validators.required,
        Validators.pattern(Regex.duration),
        DurationComponent.zeroDurationValidator
    ]);

    constructor(
        @Self()
        @Optional()
        public ngControl: NgControl
    ) 
        if (ngControl) 
            ngControl.valueAccessor = this;
        
    

    static zeroDurationValidator(control: AbstractControl): ValidationErrors 
        return control.value === '0m' ||
            control.value === '00m' ||
            control.value === '0h' ||
            control.value === '00h' ||
            control.value === '0h 0m' ||
            control.value === '0h 00m' ||
            control.value === '00h 00m' ||
            control.value === '00h 0m'
            ?  zeroDurationError: true 
            : null;
    

    onChangeCallback = (value: string) => ;
    onTouchCallback = () => ;

    ngOnInit(): void 
        this.initializeFilters();
    

    initializeFilters() 
        this.filteredDurations = this.durationControl.valueChanges.pipe(
            tap(value => this.onChangeCallback(value)),
            map(value => this.filterDurations(value))
        );
    

    onBlur() 
        this.onTouchCallback();
    

    registerOnChange(fn: any): void 
        this.onChangeCallback = fn;
    

    registerOnTouched(fn: any): void 
        this.onTouchCallback = fn;
    

    writeValue(obj: any): void 
        this.durationControl.setValue(obj, 
            emitModelToViewChange: true
        );
        this.onChangeCallback(obj);
    

    private filterDurations(value: string) 
        return this.durations.filter(duration => duration.indexOf(value) === 0);
    

duration.component.html

<mat-form-field>
    <input [formControl]="durationControl"
    type="text"
    matInput
    autocomplete="off"
    [placeholder]="'DURATION' | translate"
    [matAutocomplete]="durationAutocomplete"
    >
    <mat-error *ngIf="durationControl.hasError('required')"> 'FORM_VALIDATION.REQUIRED' | translate </mat-error>
    <mat-error *ngIf="durationControl.hasError('pattern')"> 'FORM_VALIDATION.INVALID_FORMAT' | translate </mat-error>
    <mat-error *ngIf="durationControl.hasError('zeroDurationError')"> 'FORM_VALIDATION.ZERO_DURATION_ERROR' | translate </mat-error>

    <mat-autocomplete #durationAutocomplete="matAutocomplete">
        <mat-option *ngFor="let duration of filteredDurations | async" [value]="duration">
             duration 
        </mat-option>
    </mat-autocomplete>
</mat-form-field>

【问题讨论】:

你可以使用markAsUntouched @mehany 这不是在重置任何东西,是吗? 【参考方案1】:

重置表单时,所有控件在 writeValue 方法中都接收到 null。然后我可以像这样重置 durationControl:

writeValue(obj: any): void 
    if (obj === null) 
        this.durationControl.reset();
    

    this.durationControl.setValue(obj, 
        emitModelToViewChange: true
    );
    this.onChangeCallback(obj);

【讨论】:

什么?这意味着我不能将表单控件的值设置为null?我真的怀疑这是一个好的解决方案。 这是一个很棒的解决方案。作为对上述评论的回应,无论如何,将 ANY 控件设置为 null 都意味着它在 UI 上看起来很清晰,因此如何执行它并没有什么区别。如果您确实需要将该值设置为 null 以发送到后端或其他内容,您可以随时更改在提交调用后处理中收到的对象。 非常优雅。 .reset()【参考方案2】:

组件声明必须包含 provider NG_VALUE_ACCESSOR。 在您的特定组件中,这看起来像:

@Component(
    selector: 'my-duration',
    templateUrl: 'duration.component.html',
    changeDetection: ChangeDetectionStrategy.OnPush,
    providers: [
        
          provide: NG_VALUE_ACCESSOR,
          useExisting: forwardRef(() => DurationComponent),
          multi: true
        
      ]
)

【讨论】:

我在使用另一种方式,在构造函数中: if (ngControl) ngControl.valueAccessor = this; 我正在使用您描述的提供程序,但表单重置不会重置我的组件。

以上是关于如何使用 Angular 重置自定义表单控件的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2 自定义表单输入

验证不会传播到 Angular 中的自定义表单控件 ng-select

伪元素表单控件默认样式重置与自定义大全

Angular2自定义表单控件防止发射事件

Angular 反应式表单自定义控件异步验证

Angular 2 - 单元测试绑定到嵌套的自定义表单控件