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); } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |