How can I efficiently create an editForm in Angular?

Within my database, there are numerous users, each with their own collection of recipes. Each recipe contains various properties and a list of ingredients. Take a look at the screenshot below:

Recipe with all properties

When a user goes to edit a recipe on a page, a form should be displayed with the current data pre-loaded. While this functionality is mostly working, I believe it can be improved.

I have a form that works well without arrays, specifically the array of ingredients. How can I incorporate ingredients into my edit form?

I would greatly appreciate it if you could review my code and provide feedback and suggestions for improvement.

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();  
  }

  loadRecipe() {
    this.recipeService.getRecipe(this.route.snapshot.params.id).subscribe(recipe => {
      this.recipe = recipe;
      this.initializeUploader();
    })
  }
  updateRecipe(id: number) {
    this.recipeService.editRecipe(id, this.recipe).subscribe(next => {
      this.toastr.success('Recipe updated successfully');
      this.editForm.reset(this.recipe);
    }, error => {
      this.toastr.error(error);
    });
  }

}

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>
          <input class="form-control" type="text" name="name" [(ngModel)]="recipe.name">
        </div>
        <div class="form-group">
          <app-ingredient-editor [ingredients] = "recipe.ingredients"></app-ingredient-editor>
          <div *ngFor="let ingredient of recipe.ingredients; let i = index">

            <input class="form-control" type="text" name="{{ingredient.name}}" [(ngModel)]="ingredient.name">
            <input class="form-control" type="text" name="{{ingredient.amount}}" [(ngModel)]="ingredient.amount">
          </div>
        </div>
        <div class="form-group">
          <br>
          <p>Add recipes</p>
        </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>  

    <button [disabled]="!editForm.dirty" form="editForm" class="btn btn-success btn-block mb-5 mt-5">Save changes</button>
  </div>

Currently, it appears like this:

Form on page

When I delete an ingredient name while making changes, I encounter the following error in the console:

recipe-edit.component.html:12 ERROR Error: If ngModel is used within a form tag, either the name attribute must be set or the form
      control must be defined as 'standalone' in ngModelOptions.

The problematic part of the code is:

 <div *ngFor="let ingredient of recipe.ingredients; let i = index">
    
                <input class="form-control" type="text" name="{{ingredient.name}}" [(ngModel)]="ingredient.name">
                <input class="form-control" type="text" name="{{ingredient.amount}}" [(ngModel)]="ingredient.amount">
              </div>
            </div>

However, I am unsure of how to resolve this issue.

How can I incorporate an array into a template-driven form? In my scenario, I need to display the current ingredients and allow for editing.

I have attempted something like this :

<input class="form-control" type="text" name="ingredient[i].name" [(ngModel)]="ingredient[i].name">
            <input class="form-control" type="text" name="ingredient[i].amount" [(ngModel)]="ingredient[i].amount">

Unfortunately, it does not work.

Answer №1

The issue here lies with the necessity of defining the property name on the form for Angular to correctly identify which input to update. By binding the name to the same property as the editable model, users are able to inadvertently modify or delete it, creating potential problems.

To resolve this, it is essential to assign a unique value that remains constant. Here's a revised version that should resolve the issue:

<div *ngFor="let ingredient of recipe.ingredients; let i = index">
    <input class="form-control" type="text" name="name{{ingredient.id}}" [(ngModel)]="ingredient.name">
    <input class="form-control" type="text" name="amount{{ingredient.id}}" [(ngModel)]="ingredient.amount">
  </div>
</div>

You can view the updated implementation in action on StackBlitz here: https://stackblitz.com/edit/angular-10-base-template-q243lw?file=src%2Fapp%2Fapp.component.html

Edit: Corrected a bug in the original post and included a link to the StackBlitz example.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

React.js: The function useDef has not been defined

Attempting to create a React.js calculator application, my initial step was to delete the entire src folder and replace it with a new one containing the necessary elements for the web app. Here is the content of the index.js file: import React,{ useEffect, ...

Dealing with a series of challenging npm problems consecutively

npm WARNING: Deprecated Usage Please note that global '--global' and '--local' configurations are now deprecated. Please use '--location=global' instead. Error Code: ERESOLVE Unable to resolve dependency tree. While reso ...

retrieve data from URL parameters (navigation backward)

When navigating from the main page to the transaction page and then to the details page, I have implemented a go back feature on the details page. Using the state, I pass data when navigating back so that I can access it again from the transaction page. H ...

No output returned by SwitchMap in Angular 2

I'm facing an issue with my SwitchMap implementation and struggling to resolve it. The problem occurs when I have a collection of category buttons that, when clicked, trigger a server call to load the corresponding data. However, if I switch between t ...

Creating code to ensure a div element is displayed only after a specific duration has elapsed

Can anyone help me with coding to make a div appear on a website after a specific amount of time? Here's an example of what I'm trying to achieve: <div class="container"> <div class="secretpopout"> This is the hidden div that should ...

Rendering HTML is not supported by AngularJS on Android 19 with version 4.4.4 and Safari 8.0.5

I have been working on an AngularJS app that is being displayed in a Webview on Android. Everything was functioning properly until yesterday when I started encountering issues with Angular not rendering the DOM correctly. I have tested the app on various v ...

Unable to retrieve information from a function in Vue.js (using Ionic framework)

When attempting to extract a variable from a method, I encounter the following error message: Property 'commentLikeVisible' does not exist on type '{ toggleCommentLikeVisible: () => void; This is the code I am working with: <template& ...

Dim the brightness of an image on Internet Explorer

I discovered this code on another site and it functions flawlessly in Chrome and FF, however, IE (version 11.0.9) doesn't seem to like it. -webkit-filter: grayscale(0%); -moz-filter: grayscale(0%); -o-filter: grayscale(0%); filter: grayscale(0%); fil ...

What impact do passing children have on the occurrence of Typescript errors?

Recently, I came across a peculiar situation where the Typescript compiler appeared to be confused by passing the children prop to a component, leading to unsafe behavior. I am looking to create a component that can only accept the subtitle (text) and sub ...

Attach the Bootstrap-Vue modal to the application's template

I am implementing a custom modal using bootstrap-vue modal in my project on codesandbox. This is the template structure: <template> <b-button variant="link" class="btn-remove" @click="removeItemFromOrder(index)"> Remove item </b-bu ...

Retrieving the output value from a callback function in Sqlite3

In my current project, I am using Sqlite3 with an Express backend along with a React frontend. My goal is to verify if a user with a specific email exists in the database. While working on the function provided below, which is still a work in progress, I e ...

What is the best way to import my json information into an HTML table and customize the rows as radio buttons using only Javascript?

I am facing an issue with processing a JSON response: var jsondata = dojo.fromJson(response); There is also a string that I am working with: var jsonString = jsondata.items; The variable jsonString represents the JSON data in the file: jsonString="[ ...

Change the state of items in a React component to either disabled or active depending on the active items list retrieved from the API

Obtained from the API, I have a collection of buttons that are displayed for filtering: For instance: button2 button4 button5 Assuming there are a total of 5 buttons. button1 and button3 are supposed to be in a disabled or inactive state (appearing ...

watcher using RxJS

After recently diving into the Observable and Observer pattern, I came across various resources that describe Observable as a producer and Observer as a consumer. However, as I analyzed the code snippet below, I found myself puzzled by the role of the obse ...

Tailored production method based on specific criteria

One of my challenges is to create a versatile factory method using typescript. The main objective is to initialize a class based on its name using generics, instead of employing if/else or switch statements. I am aiming for similar functionality as this ...

The issue with IONIC/Angular lies in its inability to properly render the JSON data retrieved

I have recently started learning IONIC/Angular and Javascript, although I do have some background in other programming languages. The issue I'm currently facing is related to fetching JSON data from an external API (I've created the API with Stra ...

What are the reasons for a jQuery function to run in a selective manner?

There seems to be some inconsistency in the behavior of this incomplete script that I'm trying to debug. The issue arises when I click off an item, as sometimes the $(editObj).removeAttr('style'); line of code executes and other times it doe ...

Implementing Node.js with browser cache and handling 304 responses

I am currently in the process of creating a single page application with a standard HTML layout as shown below: <html> <head> <title>...</title> <link rel="stylesheet" media="all" type="text/css" href="css/main.css"> ...

Every time an action is carried out in the app, React generates countless TypeError messages

Whenever I'm using the application (particularly when started with npm start), my console gets flooded with thousands of TypeError messages like this: https://i.sstatic.net/3YZpV.png This issue doesn't occur when I build the app... It's fr ...

Combine multiple values into a single input in the number field

I have a number type input field... What I am looking for is: Age-Height-Weight 20-180-80 Is there a way to ensure that users input data in this exact format and then have the final result inserted into the input field with type="number" and submitted? ...