【问题标题】:How to get form values from dynamic HTML In angular2如何在angular2中从动态HTML中获取表单值
【发布时间】:2016-12-05 21:27:17
【问题描述】:

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

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

如果您查看示例它在模板中有 3 个输入 2(名称、姓氏)。我正在使用 addcomponent 注入地址。

我正在使用 Form Builder 构建所有 3 个控件,但是当您单击提交时,您可以看到值 Name & Last Name 出现,但无法获取地址的值。

我现在不确定如何获取这些值。我请求社区专家帮助我。

http://plnkr.co/edit/fcS1hdfLErjgChcFsRiX?p=preview

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);
  }
}

Plunker 不起作用,所以我在 stackbliz 中添加了示例。

https://stackblitz.com/edit/angular-t3mmg6

这个例子是动态表单控件在添加组件中(这是你可以从服务器获取表单控件的地方)。如果您看到 addcomponent 方法,您可以看到表单控件。在这个例子中,我没有使用角材料,但它可以工作(我正在使用@work)。这是 Angular 6 的目标,但适用于所有以前的版本。

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

谢谢

维杰

【问题讨论】:

    标签: angular typescript


    【解决方案1】:

    问题是您将组地址添加到父组件中的表单构建器组,但 html 被添加为无法更新表单组值的子组件。

    使用父子方式,当值发生变化时,需要将值的变化从子组件输出到父组件,然后手动设置表单组的值,看看这里有一些不同的方式父子组件之间的通信:https://angular.io/docs/ts/latest/cookbook/component-communication.html

    对我来说,如果你可以使用 ngFor 或 ngIf 指令来控制你的动态表单而不是添加子组件,它看起来会更容易。在这里查看如何执行此操作的示例:https://angular.io/docs/ts/latest/cookbook/dynamic-form.html

    【讨论】:

    • @hanger 感谢您的回复,但我的情况是我没有在这里使用动态表单工作的奢侈。 html 来自表格,需要对其进行操作以添加要呈现给用户以填写的输入。我知道我在顶部添加了表单组,我不确定在动态 HTML 之后如何处理它显示。我研究过动态表单,但动态表单无法正常工作,因为表单输入的内容是数据库中 html 的一部分,需要动态显示。
    • 如果您从数据库中获取 html,您可以将 div 的 innerHtml 属性绑定到您的 html 字符串以显示 html。但是,如果此解决方案不起作用,那么您需要使用原始答案的 Angular Cookbook 中概述的父子之间进行通信的一种方式。
    • 我尝试使用内部 html 进行绑定,它工作正常,它显示了 html 和输入值,但问题是我无法将值返回给父级。这就是我需要帮助的地方。
    • 好的,您是否介意更新您的帖子以显示您在绑定到内部 html 时的表现。需要明确的是 - 我谈论使用这样的 div 更新您的模板:
      。这种方法没有父子关系,因为 html 字符串和值都将在父级上。
    • 给你。这是使用 div InnerHTML。我使用的是 safeHTML,所以值输入字段没有条纹plnkr.co/edit/Pgd81dg5tBuraSXkDBe6?p=preview 当您添加一些值并提交时,您将不会获得地址的值。这是我遇到的问题
    【解决方案2】:

    我的同事 (Justin) 帮助我了解如何从动态 HTML 访问表单值。 @Hagner (http://plnkr.co/edit/DeYGuZSOYvxT76YI8SRU?p=preview) 答案是您可以做到的一种方式。这涉及服务。下面的方法不涉及服务,是更直接的访问值的方法。我想我会为那些有这些情况的人发帖。

    -- 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>
    

    http://plnkr.co/edit/rELaWPJ2cDJyCB55deTF?p=preview

    感谢贾斯汀帮助我。

    【讨论】:

    • 如果你想使用 AOT,你需要这样做 import { JitCompilerFactory } from '@angular/compiler';私有编译器:Compiler = new JitCompilerFactory([{ useDebug: false, useJit: true }]).createCompiler();在需要的地方创建编译器@。从构造函数中删除编译器。这将适用于 AOT。更新了 plnkr 以使用 AOT 编译 plnkr.co/edit/2qAWGh?p=preview
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-09
    • 2012-07-19
    • 2017-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-05
    相关资源
    最近更新 更多