我需要为表单动态创建textarea
。我有以下模型:
this.fields = {
isRequired: true,
type: {
options: [
{
label: 'Option 1',
value: '1'
},
{
label: 'Option 2',
value: '2'
}
]
}
};
并形成:
this.userForm = this.fb.group({
isRequired: [this.fields.isRequired, Validators.required],
//... here a lot of other controls
type: this.fb.group({
options: this.fb.array(this.fields.type.options),
})
});
模板的一部分:
<div formGroupName="type">
<div formArrayName="options">
<div *ngFor="let option of userForm.controls.type.controls.options.controls; let i=index">
<textarea [formControlName]="i"></textarea>
</div>
</div>
</div>
因此,正如您所看到的,我有对象数组,并且我想使用label
属性在textarea
中显示它。现在我看到[object Object]
。如果我将options
更改为简单的字符串数组,例如:['Option 1', 'Option 2']
,那么一切正常。但我需要使用对象。所以,而不是:
<textarea [formControlName]="i"></textarea>
我试过了:
<textarea [formControlName]="option[i].label"></textarea>
但是,它不起作用 我如何使用对象数组?
这是Plunkr
答案 0 :(得分:20)
您需要添加一个FormGroup,其中包含您的label
和value
。这也意味着表单创建的对象与fields
对象具有相同的构建。
ngOnInit() {
// build form
this.userForm = this.fb.group({
type: this.fb.group({
options: this.fb.array([]) // create empty form array
})
});
// patch the values from your object
this.patch();
}
之后,我们使用OnInit中调用的方法修补值:
patch() {
const control = <FormArray>this.userForm.get('type.options');
this.fields.type.options.forEach(x => {
control.push(this.patchValues(x.label, x.value))
});
}
// assign the values
patchValues(label, value) {
return this.fb.group({
label: [label],
value: [value]
})
}
最后,这是一个
答案 1 :(得分:10)
AJT_82的答案对我来说非常有用,我想我会分享如何重用他的代码并构建一个类似的例子 - 一个可能有一个更常见的用例,它邀请几个人一次注册。像这样:
我认为这可能会对其他人有所帮助,所以这就是我在这里添加它的原因。
您可以看到表单是电子邮件的简单文本输入数组,每个表单都加载了自定义验证程序。您可以在屏幕截图中看到JSON结构 - 请参阅模板中的前一行(感谢AJT),这是一个非常有用的想法,同时开发以查看您的模型和控件是否已连线!
首先,声明我们需要的对象。请注意,3个空字符串是模型数据(我们将绑定到文本输入):
public form: FormGroup;
private control: FormArray;
private emailsModel = { emails: ['','','']} // the model, ready to hold the emails
private fb : FormBuilder;
构造函数是干净的(为了更容易测试,只需注入我的userService以在提交后将表单数据发送到):
constructor(
private _userService: UserService,
) {}
表单是在init方法中构建的,包括存储对emailsArray
控件本身的引用,以便稍后检查其子项(实际输入)是否被触摸,如果是,它们是否有错误:< / p>
ngOnInit() {
this.fb = new FormBuilder;
this.form = this.fb.group({
emailsArray: this.fb.array([])
});
this.control = <FormArray>this.form.controls['emailsArray'];
this.patch();
}
private patch(): void {
// iterate the object model and extra values, binding them to the controls
this.emailsModel.emails.forEach((item) => {
this.control.push(this.patchValues(item));
})
}
这是使用验证器构建每个输入控件(类型为AbstracControl):
private patchValues(item): AbstractControl {
return this.fb.group({
email: [item, Validators.compose([emailValidator])]
})
}
检查输入是否被触摸以及验证器是否引发错误的2个辅助方法(请参阅模板以了解它们的使用方法 - 请注意我从*ngFor
中传递数组的索引值模板):
private hasError(i):boolean {
// const control = <FormArray>this.form.controls['emailsArray'];
return this.control.controls[i].get('email').hasError('invalidEmail');
}
private isTouched(i):boolean {
// const control = <FormArray>this.form.controls['emailsArray'];
return this.control.controls[i].get('email').touched;
}
这是验证员:
export function emailValidator(control: FormControl): { [key: string]: any } {
var emailRegexp = /[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$/;
if (control.value && !emailRegexp.test(control.value)) {
return { invalidEmail: true };
}
}
模板:
<form [formGroup]="form" (ngSubmit)="onSubmit(form.value)" class="text-left">
<div formArrayName="emailsArray">
<div *ngFor="let child of form.controls.emailsArray.controls; let i=index">
<div class="form-group" formGroupName="{{i}}">
<input formControlName="email"
class="form-control checking-field"
placeholder="Email" type="text">
<span class="help-block" *ngIf="isTouched(i)">
<span class="text-danger"
*ngIf="hasError(i)">Invalid email address
</span>
</span>
</div>
</div>
</div>
<pre>{{form.value | json }}</pre>
<div class="form-group text-center">
<button class="btn btn-main btn-block" type="submit">INVITE</button>
</div>
</form>
为了它的价值,我开始讨论这个可怕的混乱 - 但如果你看下面的代码,你可能更容易理解上面的代码!
public form: FormGroup;
public email1: AbstractControl;
public email2: AbstractControl;
public email3: AbstractControl;
public email4: AbstractControl;
public email5: AbstractControl;
constructor(
fb: FormBuilder
) {
this.form = fb.group({
'email1': ['', Validators.compose([emailValidator])],
'email2': ['', Validators.compose([emailValidator])],
'email3': ['', Validators.compose([emailValidator])],
'email4': ['', Validators.compose([emailValidator])],
'email5': ['', Validators.compose([emailValidator])],
});
this.email1 = this.form.controls['email1'];
this.email2 = this.form.controls['email2'];
this.email3 = this.form.controls['email3'];
this.email4 = this.form.controls['email4'];
this.email5 = this.form.controls['email5'];
}
以上在模板中使用了这些div中的5个 - 不是很干!
<div class="form-group">
<input [formControl]="email1" class="form-control checking-field" placeholder="Email" type="text">
<span class="help-block" *ngIf="form.get('email1').touched">
<span class="text-danger" *ngIf="form.get('email1').hasError('invalidEmail')">Invalid email address</span>
</span>
</div>
答案 2 :(得分:0)
我认为FormControlName
无法实现。
你可以使用ngModel
..看看你修改过的plunker:
http://plnkr.co/edit/0DXSIUY22D6Qlvv0HF0D?p=preview
@Component({
selector: 'my-app',
template: `
<hr>
<form [formGroup]="userForm" (ngSubmit)="submit(userForm.value)">
<input type="checkbox" formControlName="isRequired"> Required Field
<div formGroupName="type">
<div formArrayName="options">
<div *ngFor="let option of userForm.controls.type.controls.options.controls; let i=index">
<label>{{ option.value.label }}</label><br />
<!-- change your textarea -->
<textarea [name]="i" [(ngModel)]="option.value.value" [ngModelOptions]="{standalone: true}" ></textarea>
</div>
</div>
</div>
<button type="submit">Submit</button>
</form>
<br>
<pre>{{userForm.value | json }}</pre>
`,
})
export class App {
name:string;
userForm: FormGroup;
fields:any;
ngOnInit() {
this.fields = {
isRequired: true,
type: {
options: [
{
label: 'Option 1',
value: '1'
},
{
label: 'Option 2',
value: '2'
}
]
}
};
this.userForm = this.fb.group({
isRequired: [this.fields.isRequired, Validators.required],
//... here a lot of other controls
type: this.fb.group({
// .. added map-function
options: this.fb.array(this.fields.type.options.map(o => new FormControl(o))),
})
});
}
submit(value) {
console.log(value);
}
constructor(private fb: FormBuilder) { }
addNumber() {
const control = <FormArray>this.userForm.controls['numbers'];
control.push(new FormControl())
}
}
答案 3 :(得分:0)
您可以尝试
打字稿:
ngOnInit(): void {
this.user = this.fb.group({
Title: ['1'],
FirstName: ['', Validators.required],
LastName: ['', Validators.required],
ContactNumbers: this.fb.array([
this.fb.group({
PhoneNumber: ['', [Validators.required]],
IsPrimary: [true],
ContactTypeId: [1]
})
]),
Emails: this.fb.array([
this.fb.group({
Email: ['', [Validators.required, Validators.email]],
IsPrimary: [true]
})
]),
Address: this.fb.group({
Address1: ['', Validators.required],
Address2: [''],
Town: [''],
State: ['UP'],
Country: [{ value: 'India', disabled: true }],
Zip: ['', Validators.required]
})
});
}
get Emails() {
return this.landlord.get('Emails') as FormArray;
}
添加和删除
addMoreEmail(index: number) {
if (index == 0) {
this.Emails.push(this.fb.group({ Email: ['', [Validators.required, Validators.email]], IsPrimary: [false] }));
} else {
this.Emails.removeAt(this.Emails.value.indexOf(index));
}
}
HTML
<form [formGroup]="user"
<div formArrayName="Emails">
<div *ngFor="let email of Emails.controls; let i=index">
<div class="row" [formGroup]="email">
<div class="col-sm-10">
<div class="form-group">
<label for="i" class="label">Email</label>
<input type="email" nbInput fullWidth id="i" placeholder="Email"
formControlName="Email" required>
</div>
</div>
<div class="col-sm-2 position-relative">
<nb-icon icon="{{i==0?'plus':'minus'}}-round" pack="ion"
(click)="addMoreEmail(i)">
</nb-icon>
</div>
</div>
</div>
</div></div>