Angular ng-content funktioniert nicht mit mat-form-field

Sep 15 2020

Mein Ziel:

Ich versuche, ein wiederverwendbares Mattenformfeld mit einem klaren Knopf zu erstellen.

Wie ich versucht habe, mein Ziel zu erreichen:

Ich habe eine "mat-clearable-input" -Komponente erstellt und sie folgendermaßen verwendet:

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

mat-clearable-input.component.html

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

Erwartetes Ergebnis :

Das ng-content-Tag nimmt das Label und die Eingabe und fügt sie in das mat-form-field-Tag ein.

Tatsächliches Ergebnis :

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)

Es sieht so aus, als ob mir etwas fehlt und ich das ng-content-Tag nicht richtig verwende.

Ich konnte die Dokumentation für das ng-content-Tag auf der eckigen Website nicht finden.

Vielen Dank für jede Hilfe.

BEARBEITEN NACH ANTWORT UNTEN

Also habe ich diese vorgeschlagene Methode ausprobiert:

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;
  }

}

Wenn ich versuche, dies in einem Formular zu verwenden, schlägt dies leider immer noch mit dem Fehler "Fehler: mat-form-field muss ein MatFormFieldControl enthalten" fehl.

Code, in dem ich versuche, diese Komponente in einem Formular zu verwenden:

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

Repro auf Stackblitz: https://stackblitz.com/edit/angular-material-starter-xypjc5?file=app/clearable-form-field/clearable-form-field.component.html

Beachten Sie, dass die Funktionen des Mat-Form-Felds nicht funktionieren (keine Gliederung, keine schwebende Beschriftung). Öffnen Sie auch die Konsole und Sie sehen den Fehler. Fehler: Das Mat-Form-Feld muss ein MatFormFieldControl enthalten.

BEARBEITEN NACH OPTION 2 WURDE VERÖFFENTLICHT

Ich habe es versucht:

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

Es funktioniert, aber dann, wenn ich meinem Formularfeld eine Mattenbezeichnung hinzufügte, wie folgt:

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

Das Etikett schwebt nie und bleibt die ganze Zeit nur normal.

Also habe ich versucht, dem this._matFormField._control._labeluntergeordneten Inhalt mit dem Label zuzuweisen, aber das hat nicht funktioniert, weil _label privat ist und es keinen Setter dafür gibt.

Es sieht so aus, als hätte ich kein Glück und dies kann in Angular nicht ohne großen Aufwand durchgeführt werden.

Wenn Sie weitere Ideen haben, können Sie den Stackblitz einfach abspalten und ausprobieren!

Nach @evilstiefel Antwort bearbeiten

Die Lösung funktioniert nur für native <input matInput>. Wenn ich versuche, das durch meine benutzerdefinierte Eingabekomponente zu ersetzen, funktioniert es nicht mehr.

Arbeitsaufbau:

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

Gleiches Setup, aber mit meiner benutzerdefinierten "gepufferten Eingabe" -Komponente (funktioniert nicht :()

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

Die Konsole protokolliert diesen Fehler, wenn ich auf die Schaltfläche Löschen klicke:

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)

Antworten

1 evilstiefel Sep 29 2020 at 09:16

Eine andere Lösung ist die Verwendung einer Direktive zur Implementierung des Verhaltens.

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();
    }
  }
}

Dadurch wird eine aufgerufene Direktive appClearableund eine optionale Komponente für ein Fallback-Layout erstellt. declarationsStellen Sie sicher, dass Sie die Komponente und die Direktive zum Array Ihres Moduls hinzufügen . Sie können entweder eine Vorlage angeben, die für die Bereitstellung der Benutzeroberfläche verwendet werden soll, oder die einfach ClearButtonComponentals Einheitslösung verwenden. Das Markup sieht folgendermaßen aus:

<!-- 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>

Dies funktioniert mit und ohne ngControl / FormControl, aber Sie müssen es möglicherweise an Ihren Anwendungsfall anpassen.

2 Akash Sep 24 2020 at 12:46

Update :

Option 1 funktioniert nicht für neue Winkelversionen, da @ViewChild()im ngOnInit()Hook undefiniert zurückgegeben wird . Ein weiterer Hack ist die Verwendung eines Dummys MatFormFieldControl-

Option 2

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

Bearbeiten :

Dieser Fehler wird ausgelöst, weil die MatFormFieldKomponente den untergeordneten Inhalt abfragt, der @ContentChild(MatFormFieldControl)nicht funktioniert, wenn Sie verschachtelt verwenden ng-content( MatFormField verwendet auch die Inhaltsprojektion ).

Option 1 (veraltet)

Im Folgenden erfahren Sie, wie Sie es zum Laufen bringen können.

@Component({
  selector: 'mat-clearable-input',
  template: `
    <mat-form-field>
      <ng-content></ng-content>
    </mat-form-field>
  `
})
export class FieldComponent implements OnInit { 
    @ContentChild(MatFormFieldControl) _control: MatFormFieldControl<any>;
    @ViewChild(MatFormField) _matFormField: MatFormField;

    ngOnInit() {
        this._matFormField._control = this._control;
    }
}

Bitte checke diesen stackBlitz aus . Außerdem gibt es dieses Problem in erstellt githubbereits.