#angular #angular-reactive-forms
#angular #угловые-реактивные-формы
Вопрос:
Обновление: Я думаю, что я приближаюсь. Это то, что у меня есть сейчас:
songEditForm = this.fb.group({
title: [null, [Validators.required, Validators.maxLength(128)]],
projectId: [null, [Validators.required]],
artist: [null, [Validators.required, Validators.maxLength(128)]],
album: [null, [Validators.maxLength(128)]],
minutes: [null, [Validators.min(0), Validators.max(99)]],
seconds: [null, [, Validators.min(0), Validators.max(59)]],
songParts: [null, [Validators.maxLength(4000)]],
timeSignature: [null, [Validators.maxLength(10)]],
songKey: [null, [Validators.maxLength(10)]],
bpm: [null, [, Validators.min(0), Validators.max(320)]],
rating: [null, [, Validators.min(0), Validators.max(5)]],
comfortLevel: [null, [, Validators.min(0), Validators.max(5)]],
energyLevel: [null, [, Validators.min(0), Validators.max(11)]],
notes: [null, [Validators.maxLength(512)]],
genre: [null],
isPublic: [null],
isFavorite: [null],
customSongProperties: this.fb.array([])
});
get customSongProperties() {
return this.songEditForm.get('customSongProperties') as FormArray;
}
<mat-card formArrayName="customSongProperties" *ngFor="let customSongProperty of customSongProperties.controls; let i=index">
<mat-form-field>
<mat-label>{{customSongProperty.value.label}}</mat-label>
<input matInput type="text" [formControlName]="i" name="i">
</mat-form-field>
</mat-card>
Но, похоже, я не могу связать значения из моего массива с массивом form.
ОРИГИНАЛЬНОЕ СООБЩЕНИЕ ПОД ЭТОЙ СТРОКОЙ
Мне нужно выполнить цикл по объекту / массиву и создать ноль или более полей ввода с метками. Объект, к которому я хочу привязать массив Reactive form, имеет свойства label и value (среди прочих). Я чувствую, что я близок, но я получаю это сообщение об ошибке:
ОШИБКА Ошибка: Не удается найти элемент управления с путем: ‘customSongProperties -> 0 -> значение’
<ng-container formArrayName="customSongProperties">
<mat-card *ngFor="let _ of customSongProperties.controls; index as i">
<ng-container [formGroupName]="i">
<input matInput formControlName="value.value" name="index" placeholder="value.label" maxlength="50" />
</ng-container>
</mat-card>
</ng-container>
Вот как я пытаюсь заполнить массив форм:
this.data.customSongProperties.forEach(customSongProperty => {
this.customSongProperties.push(new FormControl(customSongProperty));
});
Это объект, к которому я привязываюсь и пытаюсь создать поля формы из:
export class CustomSongProperty {
id: number;
userId: number;
songPropertyDataTypeId: number;
songPropertyDataTypeName: string | null;
label: string | null;
songId: number;
value: string | null;
}
Мне это кажется правильным, но явно это не так.
Я следовал этому руководству:
Руководство по массиву реактивных форм
Но в конце мое понимание как бы развалилось.
Любая помощь приветствуется.
Спасибо
Ответ №1:
Джейсон, ты можешь создать FormArray из FromControls или FormArray из FormGroups (если элементы массива form обладают уникальным свойством или они являются объектами). например
//e.g. you need a FormArray of FormControls if your json object is like
title:'my title'
customSongProperties:[ 'one','two','three']
//e.g. you need a FormArray of FormGroups if your json object is like
title:'my title'
customSongProperties:[ {value:'one'},{value:'two'},{value:'three'}]
С помощью FormArray FormControls, который вы используете
<div formArraName="customSongProperties">
<mat-card *ngFor="let customSongProperty of customSongProperties.controls;
let i=index" >
<mat-form-field>
<mat-label>{{customSongProperty.value.label}}</mat-label>
<!--you use [formControlName]="i" for the
uniq FormControl in the formArray-->
<input matInput type="text" [formControlName]="i" >
</mat-form-field>
</mat-card>
</div>
Но в вашем случае у вас есть FormArray FormGroups, поэтому .html должен быть
<div formArraName="customSongProperties">
<!--see that you indicate [formGroupName]="i"-->
<mat-card *ngFor="let customSongProperty of customSongProperties.controls;
let i=index" [formGroupName]="i">
<mat-form-field>
<mat-label>{{customSongProperty.value.label}}</mat-label>
<!--you use formControlName="nameOfProperty"
remember that you can has severals FormsControls in the
FormGroup
-->
<input matInput type="text" formControlName="value" >
</mat-form-field>
</mat-card>
</div>
О том, как создать FormGroup, всегда интересно использовать функцию, которая возвращает FormGroup и получает в качестве данных объект или null. Поскольку наш FormArray является FormArray FormGroup, мы можем сделать
getCustomSongPropertiesFormGroup(data:any=null)
{
//if data is null we create an object by defect
data=data || {id:0,userId:0...}
return this.fb.group({
id: [data.id],
userId: [data.userId],
...
})
}
И для создания FormGroup songEditForm
getSongFormGroup(data:any=null)
{
//if data is null we create an object by defect
data=data || {title:null,projectId:null...,customSongProperties:null}
return this.fb.group({
title: [data.title, [Validators.required, Validators.maxLength(128)]],
projectId: [data.projectId, [Validators.required]],
...
customSongProperties:data.customSongProperties?
fb.array(data.customSongProperties
.map(x=>this.getCustomSongPropertiesFormGroup(x)):
[]
})
}
Попробуйте немного объяснить, что такое «карта», если у вас есть в data.customSongProperties массив объектов, вы преобразуете этот массив объектов в массив FormGroup, используя map map(x=>this.getCustomSongPropertiesFormGroup(x)
это массив, с помощью которого мы создаем FormArray.
Теперь вы можете использовать, например
//to create the form songEditForm
this.songEditForm=this.getSongFormGroup()
//to add a new element of the formArray
this.customSongProperties.push(this.getCustomSongPropertiesFormGroup())
Ответ №2:
Я думаю, в этом случае лучше использовать FormGroup, вы можете использовать имена полей для генерации элементов управления и создания FormGroup, а в шаблоне вы можете просто пройти по массиву и показать входные данные:
Компонент:
export class AppComponent implements OnInit {
fieldNames: string[] = [];
form: FormGroup;
constructor(
) {}
ngOnInit(): void {
const controls: Record<string, FormControl> = Object
.keys(customSongProperty)
.reduce( (res, fieldName) => {
this.fieldNames.push(fieldName);
res[fieldName] = new FormControl(customSongProperty[fieldName]);
return res;
}, {});
this.form = new FormGroup(controls);
}
}
шаблон:
<form [formGroup]="form">
<ng-container *ngFor="let fieldName of fieldNames">
<label>
{{ fieldName}} :
<input [formControlName]="fieldName" maxlength="50" />
</label>
</ng-container>
<div class="result">
{{ form.value | json }}
</div>
</form>
Комментарии:
1. Спасибо, я думаю, может быть, я не совсем ясно выразился. Будет создан массив пользовательских свойств, и единственное поле, которое необходимо отобразить для пользовательского ввода, — это свойство ‘value’ каждого пользовательского свойства.
Ответ №3:
в определении класса ts:
songEditForm = this.fb.group({
title: [null, [Validators.required, Validators.maxLength(128)]],
customSongProperties: this.fb.array([
this.fb.group({
id: [null],
songId: [null],
label: [null],
value: [null]
})
])
});
get customSongProperties() {
return this.songEditForm.get('customSongProperties') as FormArray;
}
setExistingCustomSongProperties(customSongProperties: CustomSongProperty[]): FormArray
{
const formArray = new FormArray([]);
customSongProperties.forEach(customSongProperty => {
formArray.push(
this.fb.group({
id: customSongProperty.id,
songId: customSongProperty.songId,
label: customSongProperty.label,
value: customSongProperty.value
}));
});
return formArray;
}
в ngOnInit:
this.songEditForm.setControl('customSongProperties', this.setExistingCustomSongProperties(this.data.customSongProperties));
в компонентном html:
<div formArrayName="customSongProperties" class="available-properties">
<mat-card *ngFor="let customSongProperty of customSongProperties.controls; let i=index" [formGroupName]="i">
<mat-form-field>
<mat-label>{{customSongProperty.value.label}}</mat-label>
<input matInput type="text" formControlName="value" [name]="i">
</mat-form-field>
</mat-card>
</div>
в onSubmit:
this.data.customSongProperties = this.songEditForm.value.customSongProperties;