2016-12-05 53 views
1

我是新的角2,我很努力地從動態HTML獲取值。我的要求是我會有一些表單輸入,我需要注入動態HTML之間,其中將包含更多的投入。如何從動態HTML獲取表單值在angular2

我已經使用@Rene漢堡包的例子創建了動態表單輸入。

如果您看一下示例,它在模板(名稱,姓氏)中有3個輸入2。我正在使用addcomponent注入地址。

我使用表單生成器來構建所有3個控件,但是當您單擊提交時,您可以看到值名稱&姓氏顯示,但無法獲取地址的值。

我現在不確定如何獲取值。我正在請求社區大師幫助我。

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

應用程序/ 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); 
    } 
} 

回答

0

我的同事(賈斯汀)幫助我如何從動態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/[email protected]/dist/css/bootstrap.min.css"> 

<script src="https://unpkg.com/[email protected]/dist/zone.js"></script> 
<script src="https://unpkg.com/[email protected]/Reflect.js"></script> 
<script src="https://unpkg.com/[email protected]/dist/system.js"></script> 
<script src="https://unpkg.com/[email protected]/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

完全學分制賈斯汀幫了我。

+0

如果你想使用AOT,你需要做什麼 從'@ angular/compiler'導入{JitCompilerFactory};編譯器=新的JitCompilerFactory([{useDebug:false, useJit:true}])。createCompiler(); 在需要的地方創建編譯器@。從構造函數中移除編譯器。 這將與AOT一起使用。 更新了plnkr以使用AOT編譯 http://plnkr.co/edit/2qAWGh?p=preview –

1

的問題是,您在父組件組地址添加到formbuilder組,但HTML添加爲一個孩子無法更新您的formgroup值的組件。

使用父子方法時,需要在值更改時手動輸出從子組件到父組件的值的更改,然後手動設置表單組的值,在此處查看一些不同的內容親子組件之間進行通信的方式:https://angular.io/docs/ts/latest/cookbook/component-communication.html

對我來說,如果你可以使用ngFor或ngIf指令來控制動態表單而不是添加子組件,它看起來更容易。在這裏看看如何做到這一點的例子:https://angular.io/docs/ts/latest/cookbook/dynamic-form.html

+0

@hanger感謝您的回覆,但我的情況是,我沒有在這裏工作的動態形式的奢侈。該html來自表中,需要進行操作以添加輸入以呈現給用戶以填充它。我明白我在頂部添加了表單組,我不知道在動態HTML之後如何處理它顯示。我曾經在動態表單上工作,但動態表單不起作用,因爲表單輸入的內容是來自數據庫的html的一部分,需要動態顯示它。 –

+0

如果您從數據庫中獲取html,則可以將div的innerHtml屬性綁定到您的html字符串以顯示html。但是,如果這個解決方案不起作用,那麼你需要使用原始答案中角色烹飪書中概述的父母與孩子之間溝通的方式之一。 – hagner

+0

我嘗試使用內部的HTML綁定,它工作正常,它顯示了HTML和輸入值,但問題是我無法將值返回給父級。那是我需要幫助的地方。 –