I'm trying to implement an edit form for a model with nested attributes (FormArray). I'm having trouble with the syntax and I'm uncertain whether I'm on the right track. The attributes for the main form work, it's the nested form I'm having problems with. Here's what I have so far.
Here I initiate the form group:
private initForm() {
this.subscription = this.expenseService.getExpense(this.id)
.subscribe(
expense => {
this.expense = expense;
this.patchForm();
}
);
this.expenseEditForm = this.fb.group({
date: '',
amount: '',
check_number: '',
debit: '',
payee_id: '',
notes: '',
expense_expense_categories_attributes:[]
});
}
Here I patch the form to set values from an object retrieved from my backend API.
private patchForm() {
this.expenseEditForm.setValue({
date: '',
amount: this.expense.amount_cents,
check_number: this.expense.check_number,
debit: this.expense.debit,
payee_id: '',
notes: this.expense.notes,
expense_expense_categories_attributes: this.fb.array([
this.setExpenseCategories(),
])
});
}
This is where I'm stuck. How do I push onto a FormArray. If I try to push, I get an error stating that push it doesn't exist on FormArray.
private setExpenseCategories() {
for ( let expenseCategories of this.expense.expense_expense_categories){
this.fb.array.push([
this.fb.group({
expense_category_id: [expenseCategories.expense_category_id, Validators.required],
amount: [expenseCategories.amount_cents]
])
});
}
}
Just in case it's needed. Here's my html.
<div
*ngFor="let expensecategoriesCtl of expenseEditForm.controls.expense_expense_categories_attributes.controls let i = index"
[formGroupName]="i"
style="margin-top: 10px;">
<md-card>
<md-select class="full-width-input"
placeholder="Expense Category"
id="expense_category_id"
formControlName="expense_category_id"
>
<md-option *ngFor="let expenseCategory of expenseCategories" value="{{expenseCategory.id}}">
{{expenseCategory.category}}
</md-option>
</md-select>
<md-input-container class="full-width-input">
<input
mdInput placeholder="Amount"
type="number"
formControlName="amount">
</md-input-container>
</md-card>
</div>
FormArray patchValue() patchValue() patches the value of FormArray . We need to pass an array to patchValue() that should match the structure of control either completely or partially. In patchValue() it is not necessary to pass an array that should exactly match the structure of control.
Validating Angular FormArray First you need to add the required validators while creating a new product form group inside the addProduct method. Now let's add a span element adjacent to the input control. Add the following CSS to the app. component.
Binding FormArray to Template We use the formArrayName directive to bind the skills form array to the div element. Now the div and anything inside the div element is bound to the skills form array. Inside the div use ngFor to loop through each element of skills FormArray.
Some changes to DeborahK's answer, since expense.expense_expense_categories
doesn't contain a primitive types, but objects. Therefore we cannot assign the values as is, but each object needs to be wrapped in a FormGroup
, just like you have attempted.
Here I have a shortened version of your code:
Build the form:
ngOnInit() {
this.expenseEditForm = this.fb.group({
notes: [''],
// notice below change, we need to mark it as an formArray
expense_expense_categories_attributes: this.fb.array([])
})
Then we call patchForm
in the callback, just like you have. That function would look like this, notice, we call this.setExpenseCategories
outside:
patchForm() {
this.expenseEditForm.patchValue({
notes: this.expense.notes,
})
this.setExpenseCategories()
}
Then comes the biggest change from your existing code, where we first assign the FormArray
to the variable control
and then we iterate your array received from backend, create a FormGroup
for each object and push the object to each FormGroup
:
setExpenseCategories(){
let control = <FormArray>this.expenseEditForm.controls.expense_expense_categories_attributes;
this.expense.expense_expense_categories.forEach(x => {
control.push(this.fb.group(x));
})
}
Then to the template, this example is without Angular Material:
<form [formGroup]="expenseEditForm">
<label>Notes: </label>
<input formControlName="notes" /><br>
<!-- Declare formArrayName -->
<div formArrayName="expense_expense_categories_attributes">
<!-- iterate formArray -->
<div *ngFor="let d of expenseEditForm.get('expense_expense_categories_attributes').controls; let i=index">
<!-- Use the index for each formGroup inside the formArray -->
<div [formGroupName]="i">
<label>Amount: </label>
<input formControlName="amount" />
</div>
</div>
</div>
</form>
Finally a
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With