我是角色2的新手,我正在努力从动态HTML中获取值。我的要求是我将有一些表单输入,我需要在其间注入动态HTML,其中包含更多输入。
我使用了@Rene Hamburger中的示例并创建了动态表单输入。
如果您查看示例它在模板(Name,LastName)中有3个输入2。我正在使用addcomponent注入地址。
我正在使用表单生成器来构建所有3个控件,但是当您单击“提交”时,您可以看到值Name&姓氏显示,但无法获取地址值。
我现在不确定如何获取值。我要求社区专家帮助我。
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);
}
}
Plunker不起作用,所以我在stackbliz中添加了这个例子。
https://stackblitz.com/edit/angular-t3mmg6
此示例是动态表单控件是添加组件(这是您可以从服务器获取Formcontrols的地方)。如果您看到addcomponent方法,则可以看到Forms控件。在这个例子中,我没有使用角度材料,但它有效(我正在使用@work)。这是针对角度6的目标,但适用于以前的所有版本。
需要为AngularVersion 5及更高版本添加JITComplierFactory。
由于
维杰
答案 0 :(得分:1)
问题是您将组地址添加到父组件中的formbuilder组,但html被添加为子组件,无法更新您的表单组值。
使用父子方法,您需要在值更改时输出值从子组件更改为父组件,然后手动设置表单组的值,请查看此处的一些不同方法在父子组件之间进行通信:https://angular.io/docs/ts/latest/cookbook/component-communication.html
对我而言,如果您可以使用ngFor或ngIf指令来控制动态表单而不是添加子组件,那么看起来会更容易。请看这里有关如何执行此操作的示例:https://angular.io/docs/ts/latest/cookbook/dynamic-form.html
答案 1 :(得分:1)
我的同事(贾斯汀)帮助我了解如何从动态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
Justin的全力以赴帮助我。