Angular:mat-form-field 必须包含一个 MatFormFieldControl

Posted

技术标签:

【中文标题】Angular:mat-form-field 必须包含一个 MatFormFieldControl【英文标题】:Angular: mat-form-field must contain a MatFormFieldControl 【发布时间】:2019-05-24 22:40:41 【问题描述】:

我正在尝试添加带有自定义电话号码输入控件的表单字段。我以https://material.angular.io/components/form-field/examples的手机为例。

代码如下:

<mat-form-field>
  <example-tel-input placeholder="Phone number" required></example-tel-input>
  <mat-icon matSuffix>phone</mat-icon>
  <mat-hint>Include area code</mat-hint>
</mat-form-field>
import FocusMonitor from '@angular/cdk/a11y';
import coerceBooleanProperty from '@angular/cdk/coercion';
import Component, ElementRef, Input, OnDestroy from '@angular/core';
import FormBuilder, FormGroup from '@angular/forms';
import MatFormFieldControl from '@angular/material';
import Subject from 'rxjs';

/** @title Form field with custom telephone number input control. */
@Component(
  selector: 'form-field-custom-control-example',
  templateUrl: 'form-field-custom-control-example.html',
  styleUrls: ['form-field-custom-control-example.css'],
)
export class FormFieldCustomControlExample 

/** Data structure for holding telephone number. */
export class MyTel 
  constructor(public area: string, public exchange: string, public subscriber: string) 


/** Custom `MatFormFieldControl` for telephone number input. */
@Component(
  selector: 'example-tel-input',
  templateUrl: 'example-tel-input-example.html',
  styleUrls: ['example-tel-input-example.css'],
  providers: [provide: MatFormFieldControl, useExisting: MyTelInput],
  host: 
    '[class.example-floating]': 'shouldLabelFloat',
    '[id]': 'id',
    '[attr.aria-describedby]': 'describedBy',
  
)
export class MyTelInput implements MatFormFieldControl<MyTel>, OnDestroy 
  static nextId = 0;

  parts: FormGroup;
  stateChanges = new Subject<void>();
  focused = false;
  ngControl = null;
  errorState = false;
  controlType = 'example-tel-input';
  id = `example-tel-input-$MyTelInput.nextId++`;
  describedBy = '';

  get empty() 
    const value: area, exchange, subscriber = this.parts;

    return !area && !exchange && !subscriber;
  

  get shouldLabelFloat()  return this.focused || !this.empty; 

  @Input()
  get placeholder(): string  return this._placeholder; 
  set placeholder(value: string) 
    this._placeholder = value;
    this.stateChanges.next();
  
  private _placeholder: string;

  @Input()
  get required(): boolean  return this._required; 
  set required(value: boolean) 
    this._required = coerceBooleanProperty(value);
    this.stateChanges.next();
  
  private _required = false;

  @Input()
  get disabled(): boolean  return this._disabled; 
  set disabled(value: boolean) 
    this._disabled = coerceBooleanProperty(value);
    this.stateChanges.next();
  
  private _disabled = false;

  @Input()
  get value(): MyTel | null 
    const value: area, exchange, subscriber = this.parts;
    if (area.length === 3 && exchange.length === 3 && subscriber.length === 4) 
      return new MyTel(area, exchange, subscriber);
    
    return null;
  
  set value(tel: MyTel | null) 
    const area, exchange, subscriber = tel || new MyTel('', '', '');
    this.parts.setValue(area, exchange, subscriber);
    this.stateChanges.next();
  

  constructor(fb: FormBuilder, private fm: FocusMonitor, private elRef: ElementRef<HTMLElement>) 
    this.parts = fb.group(
      area: '',
      exchange: '',
      subscriber: '',
    );

    fm.monitor(elRef, true).subscribe(origin => 
      this.focused = !!origin;
      this.stateChanges.next();
    );
  

  ngOnDestroy() 
    this.stateChanges.complete();
    this.fm.stopMonitoring(this.elRef);
  

  setDescribedByIds(ids: string[]) 
    this.describedBy = ids.join(' ');
  

  onContainerClick(event: MouseEvent) 
    if ((event.target as Element).tagName.toLowerCase() != 'input') 
      this.elRef.nativeElement.querySelector('input')!.focus();
    
  


  

example-tel-input-example.html

  <div [formGroup]="parts" class="example-tel-input-container">
      <input class="example-tel-input-element" formControlName="area" size="3">
      <span class="example-tel-input-spacer">&ndash;</span>
      <input class="example-tel-input-element" formControlName="exchange" size="3">
      <span class="example-tel-input-spacer">&ndash;</span>
      <input class="example-tel-input-element" formControlName="subscriber" size="4">
    </div>

但我收到以下错误:

错误错误:mat-form-field 必须包含 MatFormFieldControl。

【问题讨论】:

请确保 MatInputModule 已导入。 github.com/angular/material2/issues/7898 @KimwanOgot 您的解决方案已为我解决。谢谢 【参考方案1】:

需要import两个模块并在导入和导出部分添加。

import  MatFormFieldModule  from '@angular/material/form-field';
import  MatInputModule  from '@angular/material/input';

@NgModule (
  imports: [ MatFormFieldModule, MatInputModule ],
  exports: [ MatFormFieldModule, MatInputModule ]
)

最让大家怀念的是'/'这个角色。如果您看到 Angular Material Documentation ,他们也会错过这个(上次检查时间为 2020 年 6 月 16 日,甚至不知道他们是否更新了)。我举个例子说明一下

<!-- Wrong -->
<mat-form-field>
  <input matInput>
</mat-form-field>

<!-- Right -->
<mat-form-field>
  <input matInput />
</mat-form-field>

仔细查看sn-p。当&lt;input 开始时,它必须以/&gt; 结尾,但大多数人会错过/(反斜杠)字符。

【讨论】:

哦,哇,非常感谢,直接从页面上复制了他们的示例,但无法正常工作!【参考方案2】:

确保 MatInputModule 已导入,并且 &lt;mat-form-field&gt; 包含 &lt;input&gt;matInput / matSelect 指令。

https://github.com/angular/material2/issues/7898

【讨论】:

【参考方案3】:

导入MatInputModule,解决了我的错误

【讨论】:

【参考方案4】:

你需要将你的类指定为MatFormFieldControl的提供者

https://material.angular.io/guide/creating-a-custom-form-field-control#providing-our-component-as-a-matformfieldcontrol

@Component(
  selector: 'form-field-custom-control-example',
  templateUrl: 'form-field-custom-control-example.html',
  styleUrls: ['form-field-custom-control-example.css'],
  providers: [
     provide: MatFormFieldControl, useExisting: FormFieldCustomControlExample    
  ]
)

【讨论】:

【参考方案5】:

另外,别忘了在输入标签中写上name属性:

name="yourName"

【讨论】:

【参考方案6】:

关闭输入标签的另一个可能问题。如果您从 Angular 示例之一 (https://material.angular.io/components/datepicker/overview) 复制代码,您最终可能会得到以下代码:

<mat-form-field appearance="fill">
  <mat-label>Choose a date</mat-label>
  <input matInput [matDatepicker]="picker">
  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
  <mat-datepicker #picker></mat-datepicker>
</mat-form-field>

输入应该有结束标记(斜杠):

<input matInput [matDatepicker]="picker" />

【讨论】:

【参考方案7】:

    如果您在代码中使用任何“输入”标签和“mat-form-field”,请确保在输入标签中包含“matInput”

    如果'mat-form-field'的子标签中存在任何*ngIf,则在'mat-form-field'标签中指定*ngIf条件

【讨论】:

谢谢,很有帮助【参考方案8】:

这将解决您的问题

 import 
      MatFormFieldModule, 
      MatInputModule
     from "@angular/material";

【讨论】:

【参考方案9】:

错误提示mat-form-field 应至少包含一个从中获取输入的表单字段。

例如:matInput mat-select

在您的情况下,您可以在 example-tel-input-example.html 中的输入字段中添加 matInput 标签。您还可以将mat-form-field 移动到example-tel-input-example 组件中,并将其添加到每个matInput 字段中。

<div [formGroup]="parts" class="example-tel-input-container">
      <mat-form-field>
         <input matInput class="example-tel-input-element" formControlName="area" size="3">
      </mat-form-field>
      <span class="example-tel-input-spacer">&ndash;</span>
      <mat-form-field>
          <input matInput class="example-tel-input-element" formControlName="exchange" size="3">
      </mat-form-field>
      <span class="example-tel-input-spacer">&ndash;</span>
      <mat-form-field>
          <input matInput class="example-tel-input-element" formControlName="subscriber" size="4">
      </mat-form-field>
</div>

注意mat-iconmat-hint 不能被视为表单域

【讨论】:

以上是关于Angular:mat-form-field 必须包含一个 MatFormFieldControl的主要内容,如果未能解决你的问题,请参考以下文章

浏览器控制台中的错误 mat-form-field 必须包含 MatFormFieldControl

Angular 4 mat-form-field with mat-select

ERROR 错误:使用 API 文档时,mat-form-field 必须包含 MatFormFieldControl?

Angular 8:将 Mat-form-field 中的高度更改为特定像素数

在 Angular/Material 中设置 mat-form-field 输入样式

Angular4 mat-form-field textArea 无法启用水平调整大小