解决方案
使用解决方案使用 StackBlitz
解决方案是在父组件中创建 Reactive Form。然后使用 Angulars 依赖注入,将父组件注入到动态组件中。
通过将父组件注入到动态组件中,您将可以访问所有父组件的公共属性,包括响应式表单。此解决方案演示了能够创建和使用响应式表单来绑定到动态生成的组件中的输入。
完整代码如下
import {
Component, ViewChild, OnDestroy,
AfterContentInit, ComponentFactoryResolver,
Input, Compiler, ViewContainerRef, NgModule,
NgModuleRef, Injector, Injectable
} from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {
ReactiveFormsModule, FormBuilder,
FormGroup, FormControl, Validators
} from '@angular/forms';
@Injectable()
export class DynamicControlClass {
constructor(public Key: string,
public Validator: boolean,
public minLength: number,
public maxLength: number,
public defaultValue: string,
public requiredErrorString: string,
public minLengthString: string,
public maxLengthString: string,
public ControlType: string
) { }
}
@Component({
selector: 'app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterContentInit, OnDestroy {
@ViewChild('dynamicComponent', { read: ViewContainerRef }) _container: ViewContainerRef;
public ackStringForm: FormGroup;
public ctlClass: DynamicControlClass[];
public formErrors: any = {};
public group: any = {};
public submitted: boolean = false;
private cmpRef;
constructor(
private fb: FormBuilder,
private componentFactoryResolver: ComponentFactoryResolver,
private compiler: Compiler,
private _injector: Injector,
private _m: NgModuleRef<any>) {
this.ctlClass = [
new DynamicControlClass('formTextField', true, 5, 0, '', 'Please enter a value', 'Must be Minimum of 5 Characters', '', 'textbox')]
}
ngOnDestroy() {
//Always destroy the dynamic component
//when the parent component gets destroyed
if (this.cmpRef) {
this.cmpRef.destroy();
}
}
ngAfterContentInit() {
this.ctlClass.forEach(dyclass => {
let minValue: number = dyclass.minLength;
let maxValue: number = dyclass.maxLength;
if (dyclass.Validator) {
this.formErrors[dyclass.Key] = '';
if ((dyclass.ControlType === 'radio') || (dyclass.ControlType === 'checkbox')) {
this.group[dyclass.Key] = new FormControl(dyclass.defaultValue || null, [Validators.required]);
}
else {
if ((minValue > 0) && (maxValue > 0)) {
this.group[dyclass.Key] = new FormControl(dyclass.defaultValue || '', [Validators.required, <any>Validators.minLength(minValue), <any>Validators.maxLength(maxValue)]);
}
else if ((minValue > 0) && (maxValue === 0)) {
this.group[dyclass.Key] = new FormControl(dyclass.defaultValue || '', [Validators.required, <any>Validators.minLength(minValue)]);
}
else if ((minValue === 0) && (maxValue > 0)) {
this.group[dyclass.Key] = new FormControl(dyclass.defaultValue || '', [Validators.required, <any>Validators.maxLength(maxValue)]);
}
else if ((minValue === 0) && (maxValue === 0)) {
this.group[dyclass.Key] = new FormControl(dyclass.defaultValue || '', [Validators.required]);
}
}
}
else {
this.group[dyclass.Key] = new FormControl(dyclass.defaultValue || '');
}
});
this.ackStringForm = new FormGroup(this.group);
this.ackStringForm.valueChanges.subscribe(data => this.onValueChanged(data));
this.onValueChanged();
this.addComponent();
}
private addComponent() {
let template = ` <div style="border: solid; border-color:green;">
<p>This is a dynamic component with an input using a reactive form </p>
<form [formGroup]="_parent.ackStringForm" class="form-row">
<input type="text" formControlName="formTextField" required>
<div *ngIf="_parent.formErrors.formTextField" class="alert alert-danger">
{{ _parent.formErrors.formTextField }}</div>
</form><br>
<button (click)="_parent.submitForm()"> Submit</button>
<br>
</div>
<br>
`;
@Component({
template: template,
styleUrls: ['./dynamic.component.css']
})
class DynamicComponent {
constructor(public _parent: AppComponent) {}
}
@NgModule({
imports: [
ReactiveFormsModule,
BrowserModule
],
declarations: [DynamicComponent]
})
class DynamicComponentModule { }
const mod = this.compiler.compileModuleAndAllComponentsSync(DynamicComponentModule);
const factory = mod.componentFactories.find((comp) =>
comp.componentType === DynamicComponent
);
const component = this._container.createComponent(factory);
}
private onValueChanged(data?: any) {
if (!this.ackStringForm) { return; }
const form = this.ackStringForm;
for (const field in this.formErrors) {
// clear previous error message (if any)
this.formErrors[field] = '';
const control = form.get(field);
if ((control && control.dirty && !control.valid) || (this.submitted)) {
let objClass: any;
this.ctlClass.forEach(dyclass => {
if (dyclass.Key === field) {
objClass = dyclass;
}
});
for (const key in control.errors) {
if (key === 'required') {
this.formErrors[field] += objClass.requiredErrorString + ' ';
}
else if (key === 'minlength') {
this.formErrors[field] += objClass.minLengthString + ' ';
}
else if (key === 'maxLengthString') {
this.formErrors[field] += objClass.minLengthString + ' ';
}
}
}
}
}
public submitForm(){
let value = this.ackStringForm.value.formTextField;
alert(value);
}
}