反应式表单组件

Reactive Form Components

我们使用了很多 Angular material 字段,它们的呈现方式都是一样的。我想要一种方法来简化来自 UI 设计人员和以后维护的任何更改。

我如何创建一个组件来减少 HTML 的这个块(只是示例代码)

<div fxFlex>
  <mat-form-field>
      <mat-label>The Label</mat-label>
       <input matInput formControlName="controlName" ... >
       <mat-hint>*Required</mat-hint> 
       <mat-error *ngIf="controlName.invalid && controlName.touched">{{errorMessage}}</mat-error>
   </mat-form-field>
<div>

归结到像这样明智的事情

<custominput  formControlName="controlName" [errorMessage]="Error" ...></custominput>

我尝试按照这些教程进行操作,但没有成功。

Angular Custom Fields Blog

Angular material Custom Fields

我真的不明白如何从本质上应用到输入字段。 这是我目前剩下的。

TS 文件内容

import { Component, ChangeDetectionStrategy, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { MatInput } from '@angular/material/input';

@Component({
  selector: 'hr-textinput',
  template:`
    <div [ngClass]="labelPosition">
      <mat-label>{{label}}</mat-label>
      <mat-form-field>
          <input #inputFieldIdentifier matInput formControlName={{controlName}} id={{id}} (blur)="onTouched()"> 
          <mat-error *ngIf="controlName.invalid">{{errorMsg}}</mat-error> 
          <mat-hint  *ngIf="mandatory">*Required</mat-hint>
      </mat-form-field>
    </div>
  `,
  styleUrls: ['./textinput.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      multi:true,
      useExisting: TextinputComponent
    }
  ]
})

export class TextinputComponent implements ControlValueAccessor, OnInit {
  @ViewChild('inputFieldIdentifier') inputField: MatInput;

  @Input() controlName:  string = "";
  @Input() mandatory: boolean = false;
  @Input() label: string = "";
  @Input() errorMsg = "";                     
  
  @Input() labelPosition: string = "before";  // "before" and  "above"  are the two options available at present
  @Input() id: string = "";                   

  touched = false;
  disabled = false;

  constructor() { 
    
  }
  ngOnInit(): void {
    this.id = `${this.controlName}_ID`;
  }

  onChange = (value) => {};
  onTouched = () => {};

  writeValue(fieldValue: any): void {
    this.inputField.value = fieldValue;
  }
  registerOnChange(onChangeFn: any): void {
    this.onChange = onChangeFn;
  }
  registerOnTouched(onTouchedFn: any): void {
    this.onTouched = onTouchedFn;
  }
  setDisabledState?(isDisabled: boolean): void {
    this.inputField.disabled = isDisabled;
  }
  markAsTouched() {
    if (!this.touched) {
      this.onTouched();
      this.touched = true;
    }
  }
}

SCSS 文件内容

.above {
  display: flex;
  flex-direction: column;
  justify-content: space-between;  
  align-items: left;
  .mat-form-field {
    padding-top: 6px;  
  }
}

.before {
  display: flex;
  flex-direction: row;
  justify-content: space-between;  
  align-items: center;
  .mat-form-field {
    padding-left: 12px;  
  }
}

非常感谢任何想法,因为我显然不知道。

StackBlitz Demo of Issue

您看到 The app cannot find my 'form' 错误的原因是因为您在本应实现 formControlName 接口的组件中使用了 formControlName 指令。如果您从自定义输入组件模板中删除 formControlName 指令,您将不会再看到该错误。

换句话说,在实现 ControlValueAccessor 时,您实际上是在告诉 Angular 您的组件知道如何使用 formControlName 指令,因此 formControlName 指令应该是实例化时放置您的自定义组件。即

<hr-textinput formControlName="myField">
</hr-textinput>

我分叉了你的 stackblitz 以显示工作形式:https://stackblitz.com/edit/angular-ivy-onycez?