如何在angular2中从动态HTML获取表单值

如何在angular2中从动态HTML获取表单值,angular,typescript,Angular,Typescript,我是angular 2的新手,我正在努力从动态HTML获取值。我的要求是我将有一些表单输入,我需要在其中插入动态HTML,其中将包含更多的输入 我使用了@Rene Hamburger中的示例并创建了动态表单输入 如果您查看该示例,它在模板中有3个输入2(Name,LastName)。我正在使用addcomponent注入地址 我正在使用Form Builder构建所有3个控件,但是当您单击submit时,您会看到Name&Last Name的值显示出来,但无法获取address的值 我现在不知道

我是angular 2的新手,我正在努力从动态HTML获取值。我的要求是我将有一些表单输入,我需要在其中插入动态HTML,其中将包含更多的输入

我使用了@Rene Hamburger中的示例并创建了动态表单输入

如果您查看该示例,它在模板中有3个输入2(Name,LastName)。我正在使用addcomponent注入地址

我正在使用Form Builder构建所有3个控件,但是当您单击submit时,您会看到Name&Last Name的值显示出来,但无法获取address的值

我现在不知道如何获得这些值。我请求社区权威帮助我

app/app.component.ts

import {AfterViewInit,OnInit, Compiler, Component, NgModule, ViewChild,
  ViewContainerRef} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import { FormGroup, FormControl, FormArray, FormBuilder, Validators } from '@angular/forms';
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";

@Component({
  selector: 'my-app',
  template: `
    <h1>Dynamic template:</h1>

    <form [formGroup]="myForm" (ngSubmit)="onSubmit()">
     <div  class="form-row">
      <label for="">Name</label>
      <input type="text" class="form-control" formControlName="name">
            <small [hidden]="myForm.controls.name.valid || (myForm.controls.name.pristine && !submitted)" class="text-danger">
            Name is required (minimum 5 characters).
          </small>
    </div>

        <div  class="form-row">
      <label for="">Last Name</label>
      <input type="text" class="form-control" formControlName="lastname">
            <small [hidden]="myForm.controls.name.valid || (myForm.controls.name.pristine && !submitted)" class="text-danger">
            Name is required (minimum 5 characters).
          </small>
    </div>

       <div #container></div>

      <div class="form-row">
      <button type="submit">Submit</button>
      </div>
       <div *ngIf="payLoad" class="form-row">
            <strong>Saved the following values</strong><br>{{payLoad}}
        </div>


    </form>
  `,
})
export class AppComponent implements OnInit , AfterViewInit {
  @ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;
  public myForm: FormGroup; // our model driven form
  public payLoad: string;

    public onSubmit() {
        this.payLoad = JSON.stringify(this.myForm.value);
    }

  constructor(private compiler: Compiler,private formBuilder: FormBuilder,private sanitizer: DomSanitizer) {}

ngOnInit() {
      this.myForm = this.formBuilder.group({
            name: ['', [<any>Validators.required, <any>Validators.minLength(5)]],
            lastname: ['', [<any>Validators.required, <any>Validators.minLength(5)]],
            address: ['', [<any>Validators.required, <any>Validators.minLength(5)]]
            });
}
  ngAfterViewInit() {

    this.addComponent('<div  class="form-row"> <label for="">Address</label> <input type="text" class="form-control" formControlName="address">  </div>');
  }

  private addComponent(template: string) {
    @Component({template: template})
    class TemplateComponent {}

    @NgModule({declarations: [TemplateComponent]})
    class TemplateModule {}

    const mod = this.compiler.compileModuleAndAllComponentsSync(TemplateModule);
    const factory = mod.componentFactories.find((comp) =>
      comp.componentType === TemplateComponent
    );
    const component = this.container.createComponent(factory);
  }
}
import{AfterViewInit、OnInit、编译器、组件、NgModule、ViewChild、,
ViewContainerRef}来自“@angular/core”
从“@angular/platform browser”导入{BrowserModule}
从“@angular/forms”导入{FormGroup、FormControl、FormArray、FormBuilder、Validators};
从“@angular/platform browser”导入{domsanizer,SafeHtml}”;
@组成部分({
选择器:“我的应用程序”,
模板:`
动态模板:
名称
名称是必需的(至少5个字符)。
姓
名称是必需的(至少5个字符)。
提交
保存了以下值
{{payLoad} `, }) 导出类AppComponent实现OnInit,AfterViewInit{ @ViewChild('container',{read:ViewContainerRef})container:ViewContainerRef; public myForm:FormGroup;//我们的模型驱动表单 公共有效载荷:字符串; 公共onSubmit(){ this.payLoad=JSON.stringify(this.myForm.value); } 构造函数(私有编译器:编译器,私有formBuilder:formBuilder,私有sanitizer:DomSanitizer){} 恩戈尼尼特(){ this.myForm=this.formBuilder.group({ 姓名:[''[

此示例为添加组件中的动态表单控件(这是您可以从服务器获取表单控件的位置)。如果您看到addcomponent方法,您可以看到表单控件。在此示例中,我没有使用angular材质,但它可以工作(我使用@work)。这是angular 6的目标,但在所有以前的版本中都可以工作

需要为AngularVersion 5及以上版本添加JITComplierFactory

谢谢


Vijay

问题在于您将组地址添加到父组件中的formbuilder组中,但html作为子组件添加,无法更新formgroup值

使用父子方法,当值更改时,您需要将值的更改从子组件输出到父组件,然后手动设置表单组的值。请查看此处以了解父子组件之间的一些不同通信方式:

对我来说,如果您可以使用ngFor或ngIf指令来控制动态表单,而不是添加子组件,看起来会更容易。请看下面的示例,了解如何做到这一点:

我的同事(Justin)帮助我从动态HTML访问表单值。@Hagner()答案是一种方法,你可以这样做。这涉及到服务。下面的方法不涉及服务,而是更直接的方法来访问值。我想我会发布这些场景

-- app/app.component.ts

    import {
  AfterContentInit, AfterViewInit, AfterViewChecked, OnInit, Compiler, Component, NgModule, ViewChild,
  ViewContainerRef, forwardRef, Injectable, ChangeDetectorRef
} from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { ReactiveFormsModule, FormGroup, FormControl, FormsModule, FormArray, FormBuilder, Validators } from '@angular/forms';
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";

@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: 'my-app',
  template: `
    <h1>Dynamic template:</h1>

<div class="container">
    <form [formGroup]="myForm" (ngSubmit)="onSubmit()" novalidate>



     <div  class="form-row">
      <label for="">First Name</label>
      <input type="text" class="form-control" formControlName="firstname" required>
              <div *ngIf="formErrors.firstname" class="alert alert-danger">
                {{ formErrors.firstname }}
              </div>

    </div>

        <div  class="form-row">
      <label for="">Last Name</label>
      <input type="text" class="form-control" formControlName="lastname"  required>

                            <div *ngIf="formErrors.lastname" class="alert alert-danger">
                {{ formErrors.lastname }}
              </div>
      </div>

       <div #container></div>
<!--
<div  class="form-row">

    <input type="radio" formControlName="Medical_Flu_Concent_Decline_medical_flu_concent_decline_rule1" value="1"> <b>Concent Template </b>
    <br>
    <input type="radio" formControlName="Medical_Flu_Concent_Decline_medical_flu_concent_decline_rule1" value="2"> <b>Decline  Template</b>
</div>
-->

       <br>
       <!--
            <button type="submit" class="btn btn-default"
             [disabled]="!myForm.valid">Submit</button>
       -->

                   <button type="submit" class="btn btn-default" >Submit</button>

       <div *ngIf="payLoad" class="form-row">
            <strong>Saved the following values</strong><br>{{payLoad}}
        </div>

        <div> Is Form Valid : {{myForm.valid}}</div>
    </form>

    </div>
  `
  ,
  styles: ['h1 {color: #369;font-family: Arial, Helvetica, sans-serif;font-size: 250%;} input[required]:valid {border-left: 5px solid #42A948; /* green */ } input[required]:invalid {border-left: 5px solid #a94442; /* red */ } .radioValidation input:invalid{outline: 2px solid  #a94442;} .radioValidation input:valid{outline: 2px solid #42A948;}'],

})
export class AppComponent implements AfterContentInit {
  @ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;
  public myForm: FormGroup; // our model driven form
  public payLoad: string;
  public controlData: [string, boolean, number];
  public ctlClass: DynamicControlClass[];
  public formErrors: any = {};
  public group: any = {};
  public submitted: boolean = false;
  public setValidatorValue: boolean = false;

  public onSubmit() {

    this.submitted = true;
    this.setValidatorValue = false;
    this.onValueChanged();

    if (this.myForm.valid) {

      const form = this.myForm

      const control = form.get('Medical_Flu_Concent_Decline_medical_flu_concent_decline');

      if (control) {
        if (control.value === '1') {
          const controlreset = form.get('Medical_Flu_Decline_Details_medical_flu_decline_details');

          if ((controlreset) && (controlreset.value)) {
            this.myForm.patchValue({ Medical_Flu_Decline_Details_medical_flu_decline_details: null });
          }
        }
      }
      this.payLoad = JSON.stringify(this.myForm.value);
    }

  }

  constructor(private compiler: Compiler, private formBuilder: FormBuilder, private sanitizer: DomSanitizer) {

    this.ctlClass = [
      new DynamicControlClass('firstname', true, 5, 0, '', 'Please enter First Name', 'First Name must be Minimum of 5 Characters', '', 'textbox'),
      new DynamicControlClass('lastname', true, 5, 0, '', 'Please enter LastName', 'Last Name must be Minimum of 5 Characters', '', 'textbox'),
      new DynamicControlClass('address', true, 5, 0, 'Default Address', 'Please enter Address', 'Address must be Minimum of 5 Characters', '', 'textbox'),
      new DynamicControlClass('Medical_Flu_Concent_Decline_medical_flu_concent_decline', true, 0, 0, null, 'Please Select one of the Radio option', '', '', 'radio'),
      new DynamicControlClass('Medical_Flu_Decline_Details_medical_flu_decline_details', false, 0, 0, null, 'Please Select one of the Decline option', '', '', 'radio'),
      new DynamicControlClass('city', true, 5, 0, 'Enter City', 'Please enter City', 'City must be Minimum of 5 Characters', '', 'textbox')]
  };



  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 {

        if (dyclass.Key === 'Medical_Flu_Decline_Details_medical_flu_decline_details') {
          this.formErrors[dyclass.Key] = 'null';
          this.group[dyclass.Key] = new FormControl(dyclass.defaultValue);
        }
        else {
          this.group[dyclass.Key] = new FormControl(dyclass.defaultValue || '');
        }


      }



    });

    this.myForm = new FormGroup(this.group);

    this.myForm.valueChanges.subscribe(data => this.onValueChanged(data));

    this.onValueChanged(); // (re)set validation messages now


    this.addComponent('<div [formGroup]="_parent.myForm" class="form-row">  <label for="">Address</label> <input type="text" class="form-control" formControlName="address"  required> <div *ngIf="_parent.formErrors.address" class="alert alert-danger">{{ _parent.formErrors.address }}</div><\div><div [formGroup]="_parent.myForm" class="form-row">   <label for="">City</label> <input type="text" class="form-control" formControlName="city"  required> <div *ngIf="_parent.formErrors.city" class="alert alert-danger">{{ _parent.formErrors.city }}</div><\div><div  [formGroup]="_parent.myForm" class="form-row radioValidation" > <input type="radio" formControlName="Medical_Flu_Concent_Decline_medical_flu_concent_decline"  id="Medical_Flu_Concent_Decline_medical_flu_concent_decline_1" name="Medical_Flu_Concent_Decline_medical_flu_concent_decline" value="1" required> <b>CONSENT.</b><br><br> Here is my Consent. <br><br><input type="radio"  formControlName="Medical_Flu_Concent_Decline_medical_flu_concent_decline" name="Medical_Flu_Concent_Decline_medical_flu_concent_decline" id="Medical_Flu_Concent_Decline_medical_flu_concent_decline_2" value="2" required> <b>DECLINE. </b><br/> I am  choosing to decline for the following reason: <br><br> <div *ngIf="_parent.formErrors.Medical_Flu_Concent_Decline_medical_flu_concent_decline" class="alert alert-danger">{{ _parent.formErrors.Medical_Flu_Concent_Decline_medical_flu_concent_decline }}</div></div><div [formGroup]="_parent.myForm" class="form-row"> <input type="radio" formControlName="Medical_Flu_Decline_Details_medical_flu_decline_details" id="Medical_Flu_Decline_Details_medical_flu_decline_details_1"   name="Medical_Flu_Decline_Details_medical_flu_decline_details"   value="1"  > I am not interested<br><br><input type="radio" formControlName="Medical_Flu_Decline_Details_medical_flu_decline_details" id="Medical_Flu_Decline_Details_medical_flu_decline_details_2"   name="Medical_Flu_Decline_Details_medical_flu_decline_details"   value="2"  > I have already received <br><br><input type="radio" formControlName="Medical_Flu_Decline_Details_medical_flu_decline_details" id="Medical_Flu_Decline_Details_medical_flu_decline_details_3"   name="Medical_Flu_Decline_Details_medical_flu_decline_details"   value="3"  > I am declining for other reasons<br><br><div *ngIf="_parent.formErrors.Medical_Flu_Decline_Details_medical_flu_decline_details" class="alert alert-danger">{{ _parent.formErrors.Medical_Flu_Decline_Details_medical_flu_decline_details }}</div></div>');




  }

  public onValueChanged(data?: any) {
    if (!this.myForm) { return; }
    const form = this.myForm;

    for (const field in this.formErrors) {
      // clear previous error message (if any)
      this.formErrors[field] = '';
      const control = form.get(field);


      if (field === 'Medical_Flu_Decline_Details_medical_flu_decline_details') {
        if ((this.myForm.value['Medical_Flu_Concent_Decline_medical_flu_concent_decline']) && (this.myForm.value['Medical_Flu_Concent_Decline_medical_flu_concent_decline'] === "2")) {
          control.setValidators(Validators.required);
          control.updateValueAndValidity({ onlySelf: false, emitEvent: false })
        }
        else if ((this.myForm.value['Medical_Flu_Concent_Decline_medical_flu_concent_decline']) && (this.myForm.value['Medical_Flu_Concent_Decline_medical_flu_concent_decline'] === "1")) {
          control.setValidators(null);
          control.updateValueAndValidity({ onlySelf: false, emitEvent: false })

          const controlreset = form.get('Medical_Flu_Decline_Details_medical_flu_decline_details');

          if ((controlreset) && (controlreset.value)) {
            this.myForm.patchValue({ Medical_Flu_Decline_Details_medical_flu_decline_details: null });
          }          

        }
      }



      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 + ' ';
          }
        }



      }
    }

  }



  private addComponent(template: string) {
    @Component({
      template: template,
      styles: ['h1 {color: #369;font-family: Arial, Helvetica, sans-serif;font-size: 250%;} input[required]:valid {border-left: 5px solid #42A948; /* green */ } input[required]:invalid {border-left: 5px solid #a94442; /* red */ } .radioValidation input:invalid{outline: 2px solid  #a94442;} .radioValidation input:valid{outline: 2px solid #42A948;}'],


      // alternatively:  [{provide: TemplateContainer, useExisting: forwardRef(() => AppComponent)}]
    })
    class TemplateComponent {
      constructor(public _parent: AppComponent) {

        console.log("parent component", this._parent);

      }
    }
    @NgModule({ imports: [ReactiveFormsModule, FormsModule, BrowserModule], declarations: [TemplateComponent] })
    class TemplateModule { }

    const mod = this.compiler.compileModuleAndAllComponentsSync(TemplateModule);
    const factory = mod.componentFactories.find((comp) =>
      comp.componentType === TemplateComponent
    );
    const component = this.container.createComponent(factory);
  }
}



-- app/app.module.ts


import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { COMPILER_PROVIDERS } from '@angular/compiler';

import { AppComponent }   from './app.component';


@NgModule({
  imports:      [BrowserModule, ReactiveFormsModule],
  declarations:  [AppComponent],
  bootstrap:    [ AppComponent ]
})

export class AppModule { }


-- app/main.ts

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app.module';

const platform = platformBrowserDynamic();

platform.bootstrapModule(AppModule);


-- config.js

System.config({
  //use typescript for compilation
  transpiler: 'typescript',
  //typescript compiler options
  typescriptOptions: {
    emitDecoratorMetadata: true
  },
  paths: {
    'npm:': 'https://unpkg.com/'
  },
  //map tells the System loader where to look for things
  map: {

    'app': 'app',

      '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
      '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
      '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
      '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
      '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
      '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
      '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
      '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',

      // angular testing umd bundles
      '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
      '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
      '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
      '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
      '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
      '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js',
      '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js',
      '@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js',

      // other libraries
      'rxjs':                       'npm:rxjs',
      'lodash':                       'npm:lodash/lodash.min.js',
      'angular2-in-memory-web-api': 'npm:angular2-in-memory-web-api',
      'ts':                         'npm:plugin-typescript/lib/plugin.js',
      'typescript':                 'npm:typescript/lib/typescript.js',
  },
  //packages defines our app package
  packages: {
    app: {
      main: './main.ts',
      defaultExtension: 'ts'
    },
    rxjs: {
      defaultExtension: 'js'
    }
  }
});

-- index.html

<!DOCTYPE html>
<html>

  <head>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css">

<script src="https://unpkg.com/zone.js@0.6.21/dist/zone.js"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
<script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
<script src="https://unpkg.com/typescript@1.8.10/lib/typescript.js"></script>
<script src="config.js"></script>

    <script src="config.js"></script>
    <script>
      System.import('app').catch(function(err){ console.error(err); });
    </script>     
  </head>

  <body>
    <my-app>Loading...</my-app>
  </body>

</html>
--app/app.component.ts
进口{
AfterContentInit、AfterViewInit、AfterViewChecked、OnInit、编译器、组件、NgModule、ViewChild、,
ViewContainerRef、forwardRef、可注射、ChangeDetectorRef
}从“@角度/核心”
从“@angular/platform browser”导入{BrowserModule}
从“@angular/forms”导入{ReactiveFormsModule、FormGroup、FormControl、FormsModule、FormArray、FormBuilder、Validators};
从“@angular/platform browser”导入{domsanizer,SafeHtml}”;
@可注射()
导出类DynamicControlClass{
构造函数(公钥:字符串,
公共验证器:布尔,
公共最小长度:数字,
公共maxLength:编号,
public defaultValue:string,
public requiredErrorString:string,
公共minLengthString:string,
公共maxLengthString:string,
公共控件类型:字符串
) { }
}
@组成部分({
选择器:“我的应用程序”,
模板:`
动态模板:
名字
{{formErrors.firstname}
姓
{{formErrors.lastname}

提交 保存了以下值
{{payLoad} 表单是否有效:{{myForm.Valid} ` , 样式:[h1{颜色:#369;字体系列:Arial,Helvetica,无衬线;字体大小:250%;}输入[必需]:有效的{左边框:5px实心#42A948;/*绿色*/}输入[必需]:无效的{左边框:5px实心#a94442;/*红色*/}。无线电验证输入:无效的{轮廓:2px实心#a94442;}。无线电验证输入:有效的{轮廓:2px实心#A948;}, }) 导出类AppComponent实现AfterContentInit{ @ViewChild('container',{read:ViewContainerRef})container:ViewContainerRef; public myForm:FormGroup;//我们的模型驱动表单 公共有效载荷:字符串; 公共控制数据:[字符串,布尔值,数字]; 公共类:DynamicControlClass[]; public formErrors:any={}; 公共组:any={}; 公共提交:布尔值=false; 公共SetValidator值:布尔值=false; 公共onSubmit(){ this.submitted=true; this.setValidatorValue=false; 此.onValueChanged(); if(this.myForm.valid){ const form=this.myForm const control=form.get('Medical\u Flu Concent\u defect\u Medical\u Flu Concent\u defect'); 如果(控制){ if(control.value===