Angularng-contentが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>
mat-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タグを正しく使用していません。
AngularWebサイトで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機能が機能していない(アウトラインなし、フローティングラベルなし)ことに注意してください。コンソールも開くと、エラーエラーが表示されます:mat-form-fieldにはMatFormFieldControlが含まれている必要があります。
オプション2が投稿された後の編集
私はこれをやってみました:
<mat-form-field>
<input matInput hidden>
<ng-content></ng-content>
</mat-form-field>
それは機能しますが、フォームフィールドにマットラベルを追加すると、次のようになります。
<mat-clearable-input>
<mat-label>Numero incarico</mat-label>
<buffered-input matInput formControlName="numero"></buffered-input>
</mat-clearable-input>
ラベルが浮くことはなく、通常のスパンとしてずっとそこにとどまっています。
そこでthis._matFormField._control._label、ラベルを使用してコンテンツの子に割り当てようとしましたが、_labelがプライベートであり、セッターがないため、機能しませんでした。
運が悪かったようですが、Angularでは多くの努力をしなければこれを行うことはできません。
さらにアイデアがあれば、stackblitzをフォークして試してみてください!
@evilstiefelの回答後に編集
このソリューションは、ネイティブに対してのみ機能し<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モジュールの-arrayに必ず追加してください。ユーザーインターフェイスを提供するために使用するテンプレートを指定することも、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の有無にかかわらず機能しますが、ユースケースに合わせて調整する必要がある場合があります。
更新:
オプション1は@ViewChild()、ngOnInit()フックで未定義を返すため、新しい角度バージョンでは機能しません。別のハックはダミーを使用することですMatFormFieldControl-
オプション2
<mat-form-field>
<input matInput hidden>
<ng-content></ng-content>
</mat-form-field>
編集:
このエラーはMatFormField、@ContentChild(MatFormFieldControl)ネストされたものを使用すると機能しない子コンテンツをコンポーネントがクエリするためにスローされますng-content(MatFormFieldはコンテンツプロジェクションも使用します)。
オプション1(非推奨)
以下はそれを機能させる方法です-
@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;
}
}
このstackBlitzをチェックアウトしてください。また、この問題はgithubすでに作成されています。