Я использую угловой материал для создания формы, где я зацикливаюсь с директивой * ngFor для отображения множества входных данных различных типов. Что я хочу сделать, так это вставить matInput в другой компонент и повторно использовать его внутри моей формы. Я думаю, что я близок к тому, чтобы сделать это, но я застрял более чем на 3 дня с этой ошибкой консоли:
formControlName должно использоваться с родительской директивой formGroup. Вы будете
хотите добавить директиву formGroup и передать ей существующую FormGroup
экземпляр (вы можете создать его в своем классе).
Пример:
<div [formGroup]="myGroup">
<input formControlName="firstName">
</div>
In your class:
this.myGroup = new FormGroup({
firstName: new FormControl()
});
Эта ошибка не имеет смысла для меня, потому что у меня уже есть директива [formGroup], реализованная в моей форме, как вы увидите ниже. Поэтому мне интересно, как я могу решить эту ошибку.
По сути, я до сих пор следовал руководству на официальном сайте материала: https://material.angular.io/guide/creating-a-custom-form-field-control#trying-it-out
Вот некоторый код, чтобы показать вам, что я реализовывал:
input.component.html
<input matInput
placeholder="placeholder"
formControlName="formControlName"
/>
input.component.ts
import { Component, Input, HostBinding, Self, Optional, ElementRef, Output } from '@angular/core';
import { MatFormFieldControl } from '@angular/material';
import { FormControlName, ControlValueAccessor, NgControl, FormBuilder, FormGroup } from '@angular/forms';
import { Subject } from 'rxjs';
import { FocusMonitor } from '@angular/cdk/a11y';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
class MyInput {
constructor(public ftdName: string, public ftdConformite: string, public ftdEmetteur: string,
public ftdNation: string, public ftdNonConformite: string, public ftdGed: string,
public ftdConstatExterne: string) { }
}
@Component({
selector: 'app-input',
templateUrl: './input.component.html',
styleUrls: ['./input.component.css'],
providers: [
{
provide : MatFormFieldControl, useExisting: InputComponent
}]
})
export class InputComponent implements ControlValueAccessor, MatFormFieldControl<MyInput> {
static nextId = 0;
public _placeholder: string;
public _disabled = false;
public _required = false;
public stateChanges = new Subject<void> ();
public focused = false;
public errorState = false;
public listForm: FormGroup;
onChange: any = () => {}
onTouch: any = () => {}
@HostBinding('class.floating')
get shouldLabelFloat() {
return this.focused || !this.empty;
}
get empty() {
let n = this.listForm.value;
return !n.ftdName && !n.ftdConformite && !n.ftdNonConformite && !n.ftdConstatExterne && !n.ftdEmetteur && !n.ftdGed && !n.ftdNation;
}
@HostBinding('attr.aria-describedby') describedBy = '';
setDescribedByIds(ids: string[]) {
this.describedBy = ids.join(' ');
}
@HostBinding() id = `example-myinput-${InputComponent.nextId++}`
@Input()
formControlName: FormControlName;
@Input()
get placeholder() {
return this._placeholder
}
set placeholder(plh) {
this._placeholder = plh;
this.stateChanges.next();
}
@Input()
get required() {
return this._required;
}
set required(req) {
this._required = coerceBooleanProperty(req);
this.stateChanges.next();
}
@Input()
get disabled(): boolean {
return this._disabled;
}
set disabled(value: boolean) {
this._disabled = coerceBooleanProperty(value);
this._disabled ? this.listForm.disable() : this.listForm.enable();
this.stateChanges.next();
}
constructor(@Optional() @Self() public ngControl : NgControl,
fb: FormBuilder, private fm: FocusMonitor, private elRef: ElementRef<HTMLElement>) {
this.listForm = fb.group({
'ftdName' : '',
'ftdConformite' : '',
'ftdEmetteur' : '',
'ftdNation' : '',
'ftdNonConformite' : '',
'ftdGed' : '',
'ftdConstatExterne' : ''
})
fm.monitor(elRef.nativeElement, true)
.subscribe(origin => {
this.focused = !!origin;
this.stateChanges.next();
});
if(this.ngControl != null) {
this.ngControl.valueAccessor = this
}
}
onContainerClick(event: MouseEvent) {
if ((event.target as Element).tagName.toLowerCase() != 'input') {
this.elRef.nativeElement.querySelector('input').focus();
}
}
set value(input : MyInput | null) {
this.stateChanges.next();
}
// this method sets the value programmatically
writeValue(value:any) {
this.value = value
}
// upon UI element value changes, this method gets triggered
registerOnChange(fn: any){
this.onChange = fn
}
// upon touching the element, this method gets triggered
registerOnTouched(fn: any){
this.onTouch = fn
}
ngOnDestroy() {
this.stateChanges.complete();
this.fm.stopMonitoring(this.elRef.nativeElement);
}
}
form.component.html (где я называю свой ввод данных приложения, я упрощаю код, чтобы показать только ту часть, которая касается моей проблемы.)
<div class="matContainer">
<form class="EventForm" [formGroup]="listForm" (ngSubmit)="onSubmitList()">
<ng-container *ngFor="let group of jsonArrays.groups; let last = last">
<ng-container *ngFor="let input of group.inputs">
<mat-form-field *ngIf="input.type === 'matInput'" class="colonne{{
input.colonne }}">
<app-input
placeholder="{{ input.placeholder }}"
formControlName="{{ input.formControlName }}"
></app-input>
</mat-form-field>
</ng-container>
</ng-container>
</form>
</div>
В результате я могу видеть свои входные данные, которые обычно отображаются, только если я нажимаю на первый вход (который всегда отображается) или если я изменяю размер своей страницы, я не имею ни малейшего представления об этом поведении.
Но, тем не менее, сообщение об ошибке в консоли все равно отображается независимо от того, что.
Если у вас есть какие-либо вопросы или вещи, которые я могу уточнить, не стесняйтесь, я буду рад.