I try to update my recipe which has collection of ingredients(formArray) and i have problem with that because of formArray.
I have error on console:
ERROR Error: formArrayName must be used with a parent formGroup directive
When i update recipe without formArray(ingredients) it's working fine.
Could you give me a hint ?
It's my first time when i'm working with formArrays..
My code:
Component.ts
export class RecipeEditComponent implements OnInit {
@ViewChild('editForm') editForm: NgForm;
recipe: IRecipe;
photos: IPhoto[] = [];
ingredients: IIngredient[] = [];
uploader: FileUploader;
hasBaseDropZoneOver = false;
baseUrl = environment.apiUrl;
currentMain: IPhoto;
constructor(private route: ActivatedRoute, private recipeService: RecipeService,
private toastr: ToastrService) { }
ngOnInit(): void {
this.loadRecipe();
}
Html
<div class="container mt-4 border" *ngIf="recipe">
<form #editForm="ngForm" id="editForm" (ngSubmit)="updateRecipe(recipe.id)" >
<h5 class=" text-center mt-2">Recipe details:</h5>
<div class="form-group mt-3">
<label for="city">Name</label>
<label for="city">{{recipe.id}}</label>
<input class="form-control" type="text" name="name" [(ngModel)]="recipe.name">
</div>
<div class="form-group">
<div formArrayName="ingredients"
*ngFor="let ingredient of recipe.ingredients; let i = index;">
<div formGroupName= {{i}} class="row">
<div class="form-group col-6">
<app-text-input formControlName="name" [label]='"Name"' name="ingredient[i].name"></app-text-input>
</div>
<div class="form-group col-6">
<app-text-input formControlName="amount" [label]='"Amount"' [type]="'number'" name="ingredient[i].amount"></app-text-input>
</div>
</div>
</div>
</div>
<h5 class=" text-center mt-4">Description</h5>
<angular-editor cols=100% rows="6" [placeholder]="'Your description'" [(ngModel)]="recipe.description" name="description"></angular-editor>
</form>
<h3 class="text-center">Photos</h3>
<div class="row">
<div class="col-sm-2" *ngFor="let photo of recipe.recipePhotos">
<img src="{{photo.url}}" class="img-thumbnail p-1" alt="">
<div class="text-center">
<button type="button" class="btn btn-sm mr-1 mb-2"
(click) = "setMainPhoto(photo)"
[disabled]="photo.isMain"
[ngClass] = "photo.isMain ? 'btn-danger active' : 'btn-secondary'"
>Main</button>
<button type="button" class="btn btn-sm btn-danger mb-2"
(click)="deletePhoto(photo.id)" >
<i class="fa fa-trash-o"></i></button>
</div>
</div>
</div>
<div class="row justify-content-md-center mt-5 border">
<div class="col col-sm-4">
<div class="mt-4 text-center">
Multiple
<input type="file" ng2FileSelect [uploader]="uploader" multiple="true" /><br/>
Single
<input type="file" ng2FileSelect [uploader]="uploader" />
</div>
</div>
<div class="col col-sm-6">
<div ng2FileDrop
[ngClass]="{'nv-file-over': hasBaseDropZoneOver}"
(fileOver)="fileOverBase($event)"
[uploader]="uploader"
class="card bg-faded p-3 text-center mt-3 mb-3 my-drop-zone">
<i class="fa fa-upload fa-3x"></i>
Drop Photos Here
</div>
</div>
</div>
<div class="col-md-6 mt-5" style="margin-bottom: 40px" *ngIf="uploader?.queue?.length">
<h3 class="text-center">Upload queue</h3>
<p>Queue length: {{ uploader?.queue?.length }}</p>
<table class="table">
<thead>
<tr>
<th width="50%">Name</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of uploader.queue">
<td><strong>{{ item?.file?.name }}</strong></td>
<td *ngIf="uploader.options.isHTML5" nowrap>{{ item?.file?.size/1024/1024 | number:'.2' }} MB</td>
<td *ngIf="uploader.options.isHTML5">
</tr>
</tbody>
</table>
<div>
<div>
Queue progress:
<div class="progress mb-4" >
<div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': uploader.progress + '%' }"></div>
</div>
</div>
<button type="button" class="btn btn-success btn-s"
(click)="uploader.uploadAll()" [disabled]="!uploader.getNotUploadedItems().length">
<span class="fa fa-upload"></span> Upload
</button>
<button type="button" class="btn btn-warning btn-s"
(click)="uploader.cancelAll()" [disabled]="!uploader.isUploading">
<span class="fa fa-ban"></span> Cancel
</button>
<button type="button" class="btn btn-danger btn-s"
(click)="uploader.clearQueue()" [disabled]="!uploader.queue.length">
<span class="fa fa-trash"></span> Remove
</button>
</div>
</div>
<button [disabled]="!editForm.dirty" form="editForm" class="btn btn-success btn-block mb-5 mt-5">Save changes</button>
</div>
This is how my recipe looks like with properties:
The directive formArrayName
is a ReactiveForm directive and for it to work you must have below satisfied
formGroup
ReactiveFormModule
in your moduleYou may have to do some changes to implement this, see below
name = 'Angular ' + VERSION.major;
recipe = {
id: 1,
name: 'Test Recipe',
ingredients: [{
name: 'Chicken',
amount: 5
},
{
name: 'Pasta',
amount: 50
}],
description: 'Test Description'
}
ngForm = this.fb.group({
description: [this.recipe.description],
name: [this.recipe.name],
ingredients: this.fb.array(
this.recipe.ingredients.map(
ingredient => this.fb.group({
name: [ingredient.name],
amount: [ingredient.amount]
})
)
)
})
updateRecipe() {
}
<form [formGroup]="ngForm" id="editForm" (ngSubmit)="updateRecipe()">
<h5 class=" text-center mt-2">Recipe details:</h5>
<div class="form-group mt-3">
<label for="city">Name</label>
<label for="city">{{recipe.id}}</label>
<input class="form-control" type="text" formControlName='name'>
</div>
<div class="form-group">
<div formArrayName="ingredients" *ngFor="let ingredient of recipe.ingredients; let i = index;">
<div formGroupName={{i}} class="row">
<div class="form-group col-6">
<app-text-input formControlName="name" [label]='"Name"' name="ingredient[i].name">
</app-text-input>
</div>
<div class="form-group col-6">
<app-text-input formControlName="amount" [label]='"Amount"' [type]="'number'"
name="ingredient[i].amount"></app-text-input>
</div>
</div>
</div>
</div>
<h5 class=" text-center mt-4">Description</h5>
<angular-editor cols=100% rows="6" [placeholder]="'Your description'"
formControlName='description'></angular-editor>
</form>
Instead of using FormArrays in your template, try using NgModel for input data-binding:
<div class="form-group" *ngFor="let ingredient of recipe.ingredients; let i = index;">
<div class="form-group col-6">
<input [(ngModel)]="ingredient.name" />
</div>
<div class="form-group col-6">
<input [(ngModel)]="ingredient.amount" />
</div>
</div>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.