如何获取自定义MatFormFieldControl来反映无效状态

时间:2018-10-30 11:02:52

标签: angular angular-material-7

我一直在关注有关创建自定义表单字段控件的angular-material文档: https://material.angular.io/guide/creating-a-custom-form-field-control

它方便地跳过了模板和反应形式的完整示例,因此我一直在争先恐后地尝试将其全部连接起来。

我受到了刺痛,并取得了不同的成功。尽管还有其他问题,但我首先想了解如何使此自定义字段在false时可以识别,以便执行下面看到的invalid(我删除了{{ 1}},以便可以看到<mat-error>的状态)。 *ngIf始终为invalid,但实际上该字段应为{{symbolInput.invalid}},因为该字段是必填字段!

自定义MatFormFieldControl模板的用法:

false

自定义MatFormFieldControl类:

true

symbol-input.component.html:

<mat-form-field class="symbol">
    <symbol-input
      name="symbol"
      placeholder="Symbol"
      ngModel
      #symbolInput="ngModel"
      [(ngModel)]="symbol"
      required></symbol-input>
    <button
      mat-button matSuffix mat-icon-button
      *ngIf="symbol && (symbol.asset1 || symbol.asset2)"
      aria-label="Clear"
      (click)="clearSymbol()">
      <mat-icon>close</mat-icon>
    </button>
    <mat-error >{{symbolInput.invalid}}</mat-error>
  </mat-form-field>

有人会友好地将我指向正确的方向吗?

** 已更新 ** 订阅export interface AssetSymbol { asset1: string, asset2: string } @Component({ selector: 'symbol-input', templateUrl: './symbol-input.component.html', styleUrls: ['./symbol-input.component.css'], providers: [{ provide: MatFormFieldControl, useExisting: SymbolInputComponent}] }) export class SymbolInputComponent implements MatFormFieldControl<AssetSymbol>, OnDestroy { static nextId = 0; stateChanges = new Subject<void>(); parts: FormGroup; focused = false; errorState = false; controlType = 'symbol-input'; onChangeCallback; @HostBinding() id = `symbol-input-${SymbolInputComponent.nextId++}`; @HostBinding('class.floating') get shouldLabelFloat() { return this.focused || !this.empty; } @HostBinding('attr.aria-describedby') describedBy = ''; setDescribedByIds(ids: string[]) { this.describedBy = ids.join(' '); } get empty() { let n = this.parts.value; return !n.asset1 && !n.asset2; } @Input() get value(): AssetSymbol | null { let n = this.parts.value; return { asset1: n.asset1, asset2: n.asset2}; } set value(symbol: AssetSymbol | null) { symbol = symbol || { asset1: "", asset2: ""}; this.parts.setValue({asset1: symbol.asset1, asset2: symbol.asset2}); this.stateChanges.next(); } @Input() get placeholder() { return this._placeholder; } set placeholder(plh) { this._placeholder = plh; this.stateChanges.next(); } private _placeholder: string; @Input() get required() { return this._required; } set required(req) { this._required = coerceBooleanProperty(req); this.stateChanges.next(); } private _required = false; @Input() get disabled() { return this._disabled; } set disabled(dis) { this._disabled = coerceBooleanProperty(dis); this.stateChanges.next(); } private _disabled = false; constructor( fb: FormBuilder, @Optional() @Self() public ngControl: NgControl, private fm: FocusMonitor, private elRef: ElementRef<HTMLElement>) { this.parts = fb.group({'asset1': '', 'asset2': ''}); // Setting the value accessor directly (instead of using // the providers) to avoid running into a circular import. if (this.ngControl != null) this.ngControl.valueAccessor = this; fm.monitor(elRef.nativeElement, true).subscribe(origin => { this.focused = !!origin; this.stateChanges.next(); }); this.stateChanges.subscribe(() => { this.expandInput(this.value.asset1.length); if (this.onChangeCallback) { this.onChangeCallback(this.value); if (this.required) { const symbol = this.value; if (!symbol.asset1 || !symbol.asset2) { this.errorState = true; } else { this.errorState = false; } } } }); } onContainerClick(event: MouseEvent) { if ((event.target as Element).tagName.toLowerCase() != 'input') { this.elRef.nativeElement.querySelector('input').focus(); } } ngOnDestroy() { this.stateChanges.complete(); this.fm.stopMonitoring(this.elRef.nativeElement); } onKeyup() { this.stateChanges.next(); } static ASSET1_INPUT_SIZE = 2; asset1InputSize = SymbolInputComponent.ASSET1_INPUT_SIZE; expandInput(currentSize) { //const currentSize = (event.target as HTMLInputElement).value.length; if (currentSize >= 3) { this.asset1InputSize = currentSize; } else { this.asset1InputSize = SymbolInputComponent.ASSET1_INPUT_SIZE; } } writeValue(value: any) { this.value = value; } registerOnChange(fn: any) { this.onChangeCallback = fn; } registerOnTouched(fn: any) { } } 并设置<div [formGroup]="parts" > <input class="asset asset1" formControlName="asset1" (keyup)="onKeyup()" [size]="asset1InputSize" maxlength="5"> <span class="input-spacer">&frasl;</span> <input class="asset asset2" formControlName="asset2" size="6" maxlength="5"> </div> 后,现在symbolInput.invalid标志被设置:

this.ngControl.valueChanges

请告知您是否可以改善此问题。

2 个答案:

答案 0 :(得分:1)

  

您的实现看起来不错,并且您始终将invalid称为false,因为您没有添加任何验证。

您可以为asset1asset2添加验证,因此请更改以下行

 this.parts = fb.group({'asset1': '', 'asset2': ''});

 this.parts = this.fb.group({
      asset1: ['', Validators.required],
      asset2: ['', Validators.required,  Validators.minLength(6)]
 });

答案 1 :(得分:0)

我所做的是以下内容:

    get errorState(): boolean {
        return (this.model.invalid && this.model.dirty) 
               || (this.ngControl?.invalid && this.ngControl?.dirty);
    }

其中 model 是我的本地 FormControlngControl 是父控件。所以当我的控件有错误或父控件无效时,它会返回错误状态。