angular – 如何在动态组件中使用反应形式
|
BackgroundI从包含
HTML的服务器接收客户端生成的数据,然后我使用该数据创建一个动态组件,该组件将被注入并显示在我们的客户端中.我收到的HTML可以包含一个或多个我需要通过Angular Reactive Forms绑定的输入.
尝试1:我试图通过简单地使用[innerHTML]属性并创建动态Reactive Forms来绑定到输入来解决这个问题.但是,由于使用innerHTML属性的技术限制,该方法失败.一旦HTML在浏览器中呈现,所有属性都被强制为小写文本,因此任何Angular指令或属性都会失败.例如* ngIf,* ngFor,[formGroup],formControlName等… Angular使用camelCase几乎所有东西,因此一旦它被强制为小写文本,它就会被忽略,这种方法不再是一个可行的解决方案. 尝试2:这次我试图利用Angulars NgTemplateOutlet动态地将HTML添加到组件,然后创建并绑定到Reactive Form.这起初似乎是一个很好的解决方案,但最终为了让html呈现它需要使用[innerHTML]属性,再次使这个方法无用(如我第一次尝试中所述). 尝试3:最后我发现了动态组件,这个解决方案部分工作.我现在可以成功地创建一个格式良好的Angular HTML模板,该模板在浏览器中正确呈现.然而,这只解决了我的一半要求.此时HTML按预期显示,但我无法创建一个Reactive Form并绑定到输入. ProblemI现在有一个动态组件,它生成HTML,其中包含我需要通过创建一个Reactive Form来绑定的输入. 尝试4:我尝试将所有逻辑用于在创建的动态组件中创建Reactive Form. 通过使用此方法,显示动态组件HTML,但是我收到一个新错误:“错误错误:formGroup需要一个FormGroup实例.请传入一个.” StackBlitz with error scenario 解决方法
解决方案
Working StackBlitz with solution 解决方案是在父组件中创建Reactive Form.然后使用Angulars依赖注入并将父组件注入动态组件. 通过将父组件注入动态组件,您将可以访问所有父组件公共属性,包括反应形式.此解决方案演示了能够创建和使用Reactive Form绑定到动态生成的组件中的输入. 完整代码如下 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,'','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 || '',<any>Validators.minLength(minValue)]);
}
else if ((minValue === 0) && (maxValue > 0)) {
this.group[dyclass.Key] = new FormControl(dyclass.defaultValue || '',<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);
}
}
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
