javascript - Angular ng-content 不适用于 mat-form-field

标签 javascript angular angular-material

我的目标:

我正在尝试构建一个带有清除按钮的可重复使用的 mat-form-field。

我是如何尝试实现我的目标的:

我创建了一个“mat-clearable-input”组件并像这样使用它:

<mat-clearable-input>
        <mat-label>Put a Number here pls</mat-label>
        <input matInput formControlName="number_form_control">
    </mat-clearable-input>

ma​​t-clearable-input.component.html

<mat-form-field>
    <ng-content></ng-content>
</mat-form-field>

预期结果:

ng-content 标签获取标签和输入并将它们放入 mat-form-field 标签中。

实际结果:

Error: mat-form-field must contain a MatFormFieldControl.
    at getMatFormFieldMissingControlError (form-field.js:226)
    at MatFormField._validateControlChild (form-field.js:688)
    at MatFormField.ngAfterContentChecked (form-field.js:558)
    at callHook (core.js:2926)
    at callHooks (core.js:2892)
    at executeInitAndCheckHooks (core.js:2844)
    at refreshView (core.js:7239)
    at refreshComponent (core.js:8335)
    at refreshChildComponents (core.js:6991)
    at refreshView (core.js:7248)

看来我遗漏了什么而且我没有正确使用 ng-content 标签。

我无法在 Angular 网站上找到 ng-content 标签的文档。

感谢您的帮助。

在回答以下内容后进行编辑

所以我尝试了这个建议的方法:

export class MatClearableInputComponent implements OnInit {
  @ContentChild(MatFormFieldControl) _control: MatFormFieldControl<any>;
  @ViewChild(MatFormField) _matFormField: MatFormField;
  // see https://stackoverflow.com/questions/63898533/angular-ng-content-not-working-with-mat-form-field/
  ngOnInit() {
    this._matFormField._control = this._control;
  }

}

不幸的是,当我尝试在表单中使用它时,它仍然失败并显示错误“错误:mat-form-field 必须包含 MatFormFieldControl。”

我尝试在表单中使用此组件的代码:

<mat-clearable-input>
    <mat-label>Numero incarico</mat-label>
    <buffered-input matInput formControlName="numero"></buffered-input>
</mat-clearable-input>

在 stackblitz 上重现:https://stackblitz.com/edit/angular-material-starter-xypjc5?file=app/clearable-form-field/clearable-form-field.component.html

请注意 mat-form-field 功能如何不起作用(没有轮廓,没有 float 标签),同时打开控制台,您将看到错误错误:mat-form-field 必须包含 MatFormFieldControl。

在选项 2 发布后进行编辑

我试过这样做:

<mat-form-field>
  <input matInput hidden>
  <ng-content></ng-content>
</mat-form-field>

它可以工作,但是当我在我的表单字段中添加一个 mat-label 时,如下所示:

<mat-clearable-input>
        <mat-label>Numero incarico</mat-label>
        <buffered-input matInput formControlName="numero"></buffered-input>
    </mat-clearable-input>

标签永远不会 float ,它只是一直停留在那里作为一个正常的跨度。

所以我尝试分配给 this._matFormField._control._label带有标签的子内容,但这不起作用,因为 _label 是私有(private)的,没有它的 setter 。

看起来我运气不好,如果不经过大量努力,这是无法在 Angular 中完成的。

如果您有任何进一步的想法,请随时 fork stackblitz 并尝试!

在@evilstiefel 回答后编辑

该解决方案仅适用于 native <input matInput> . 当我尝试用我的自定义输入组件替换它时,它不再起作用了。

工作设置:

<mat-form-field appClearable>
    <mat-label>ID incarico</mat-label>
    <input matInput formControlName="id">
</mat-form-field>

相同的设置,但使用我的自定义“缓冲输入”组件(不工作 :( )

<mat-form-field appClearable>
    <mat-label>ID incarico</mat-label>
    <buffered-input matInput formControlName="id"></buffered-input>
</mat-form-field>

当我单击清除按钮时,控制台会记录此错误:

TypeError: Cannot read property 'ngControl' of undefined
    at ClearableDirective.clear (clearable.directive.ts:33)
    at ClearButtonComponent.clearHost (clearable.directive.ts:55)
    at ClearButtonComponent_Template_button_click_0_listener (clearable.directive.ts:47)
    at executeListenerWithErrorHandling (core.js:14293)
    at wrapListenerIn_markDirtyAndPreventDefault (core.js:14328)
    at HTMLButtonElement.<anonymous> (platform-browser.js:582)
    at ZoneDelegate.invokeTask (zone-evergreen.js:399)
    at Object.onInvokeTask (core.js:27126)
    at ZoneDelegate.invokeTask (zone-evergreen.js:398)
    at Zone.runTask (zone-evergreen.js:167)

最佳答案

另一种解决方案是使用指令来实现行为。

import {
  AfterViewInit,
  Component,
  ComponentFactory,
  ComponentFactoryResolver,
  ContentChild,
  Directive,
  Injector,
  Input,
  Optional,
  SkipSelf,
  TemplateRef,
  ViewContainerRef,
} from '@angular/core';
import { MatFormFieldControl } from '@angular/material/form-field';


@Directive({
  selector: '[appClearable]'
})
export class ClearableDirective implements AfterViewInit {

  @ContentChild(MatFormFieldControl) matInput: MatFormFieldControl<any>;
  @Input() appClearable: TemplateRef<any>;
  private factory: ComponentFactory<ClearButtonComponent>;

  constructor(
    private vcr: ViewContainerRef,
    resolver: ComponentFactoryResolver,
    private injector: Injector,
  ) {
    this.factory = resolver.resolveComponentFactory(ClearButtonComponent);
  }

  ngAfterViewInit(): void {
    if (this.appClearable) {
      this.vcr.createEmbeddedView(this.appClearable);
    } else {
      this.vcr.createComponent(this.factory, undefined, this.injector);
    }
  }

  /**
   * This is used to clear the formControl oder HTMLInputElement
   */
  clear(): void {
    if (this.matInput.ngControl) {
      this.matInput.ngControl.control.reset();
    } else {
      this.matInput.value = '';
    }
  }
}

/**
 * This is the markup/component for the clear-button that is shown to the user.
 */
@Component({
  selector: 'app-clear-button',
  template: `
  <button (click)="clearHost()">Clear</button>
  `
})
export class ClearButtonComponent {
  constructor(@Optional() @SkipSelf() private clearDirective: ClearableDirective) { }

  clearHost(): void {
    if (this.clearDirective) {
      this.clearDirective.clear();
    }
  }
}

这会创建一个名为 appClearable 的指令和一个用于回退布局的可选组件。确保将组件和指令添加到模块的 declarations 数组中。您可以指定用于提供用户界面的模板,也可以只使用 ClearButtonComponent 作为通用的解决方案。标记如下所示:

<!-- Use it with a template reference -->
<mat-form-field [appClearable]="clearableTmpl">
  <input type="text" matInput [formControl]="exampleInput">
</mat-form-field>

<!-- use it without a template reference -->
<mat-form-field appClearable>
  <input type="text" matInput [formControl]="exampleInput2">
</mat-form-field>

<ng-template #clearableTmpl>
  <button (click)="exampleInput.reset()">Marked-Up reference template</button>
</ng-template>

无论是否使用 ngControl/FormControl,这都适用,但您可能需要根据您的用例对其进行调整。

关于javascript - Angular ng-content 不适用于 mat-form-field,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63898533/

相关文章:

javascript - jQuery 点击事件不会解除绑定(bind)

javascript - 在 Javascript 中使用 indexOf 搜索字符串

javascript - Angular 2 : Calling other functions inside call back functions

angular - 如何在 ionic 项目中使用 Ionic native - MS Adal?

javascript - Angular 垫菜单

javascript - 如何使用 md-order-by 属性对 md-data-table 上的日期列进行排序

javascript - 更改 CSS :before if and ID has a specific class

javascript - 2 秒后离开循环

angular - 在 auth-guard 的 canActivate 中使用 BehaviorSubject

angular - CdkDrag 更新位置