"Exploring the array functionality of Angular's Reactive Forms

I am encountering an issue when trying to access the products array that is located within opticanOrders inside the orderForm. Despite seeing in the console that I should reference the products array like this:

orderForm.controls.opticianOrders.controls.products.controls

It does not seem to be working.

This is my component:

  constructor(private customerService: CustomerService, private fb: FormBuilder) { }

  orderForm: FormGroup;

  ngOnInit() {
    this.orderForm = this.fb.group({
      name: [''],
      surName: [''],
      opticianOrders: this.fb.group({
        orderDescription: [''],
        products: this.fb.array([
          this.initProduct()
        ])
      }),
    });
  }

  save(model: Customer) {
    // call API to save customer
    console.log(model);
  }

  onCancel(form: NgForm){
    this.createState.emit(false);
  }

  initProduct(){
    return this.fb.group({
      name: [''],
      manufacturerName: ['']
    })
  }

  addProduct(){
    const control = <FormArray>this.orderForm.controls['products'];
    control.push(this.initProduct());
  }

  removeProduct(i: number){
    const control = <FormArray>this.orderForm.controls['products']
  }

Html

<form [formGroup]="orderForm" novalidate (ngSubmit)="save(orderForm)">

  <!-- name -->
  <div class="form-group">
      <label>Name</label>
      <input type="text" formControlName="name">
  </div>

  <!-- surName -->
  <div class="form-group">
      <label>Last Name</label>
      <input type="text" formControlName="surName">
  </div>

  <div formGroupName="opticianOrders" class="form-group">
      <label>Order Description</label>
      <input type="text" formControlName="orderDescription">
  </div>
  <div formArrayName="products">
          <div *ngFor="let product of orderForm.controls.opticianOrders.controls.products.controls; let i=index">
              <div>
                  <span>Address {{i + 1}}</span>
                  <span *ngIf="orderForm.controls.opticianOrders.controls.products.controls.length > 1" 
                      (click)="removeProduct(i)">
                  </span>
               </div>

               <div [formGroupName]="i">
                  <div>
                      <label>Product name</label>
                      <input type="text" formControlName="name">
                  </div>
              </div>
          </div>
        </div>
    <button type="submit" [disabled]="!orderForm.valid">Submit</button>
</form>

Answer №1

To implement the changes in your HTML code, follow the instructions below

<form [formGroup]="orderForm" (ngSubmit)="save()">

  <!-- name -->
  <div class="form-group">
    <label>Enter Name</label>
    <input type="text" formControlName="name">
  </div>

  <!-- surName -->
  <div class="form-group">
    <label>Last Name</label>
    <input type="text" formControlName="surName">
  </div>

  <div class="form-group">
    <label>Order Description</label>
    <input type="text" formControlName="orderDescription">
  </div>
  <div formArrayName="products">
    <div *ngFor="let product of orderForm.controls.products['controls']; let i=index">
      <div>
        <span><strong>Product {{i + 1}}</strong></span>
        <span class="fa fa-times" *ngIf="orderForm.controls['products'].controls.length > 1" (click)="removeProduct(i)">
        </span>
      </div>

      <div [formGroupName]="i">
        <div>
          <label>Product name</label>
          <input type="text" formControlName="name">
        </div>
        <div>
          <label>Product Manufacturer name</label>
          <input type="text" formControlName="manufacturerName">
        </div>
      </div>
    </div>

    <div class="margin-20">
      <a (click)="addProduct()" style="cursor: pointer; text-transform: uppercase; font-weight: 500">
        Add another Entry +
      </a>
    </div>
  </div>
  <button class="btn btn-primary" type="submit" [disabled]="orderForm.invalid">Submit</button>
</form>

Update the TS code as follows. You can test the save form method to check if it meets your requirements.

  constructor(private fb: FormBuilder) { }

  orderForm: FormGroup;

  ngOnInit() {
    this.orderForm = this.fb.group({
      name: ['', Validators.required],
      surName: ['', Validators.required],
      orderDescription: ['', Validators.required],
      products: this.fb.array([
        this.initProduct()
      ])
    });
  }

  save() {
    console.log(this.orderForm.value);
    const obj = {
      name: this.orderForm.value.name,
      surName: this.orderForm.value.surName,
      orderDescription: this.orderForm.value.orderDescription,
      opticianOrders: {
        products: this.orderForm.value.products
      },
    };

    console.log(obj);
  }

  initProduct() {
    return this.fb.group({
      name: ['', Validators.required],
      manufacturerName: ['', Validators.required]
    })
  }

  addProduct() {
    const control = <FormArray>this.orderForm.controls['products'];
    control.push(this.initProduct());
  }

  removeProduct(i: number) {
    const control = <FormArray>this.orderForm.controls['products'];
    control.removeAt(i);
  }

Answer №2

Unfortunately, the code provided in your stackblitz is not functioning correctly. It appears that you forgot to import the ReactiveFormsModule, misplaced the forms code in the hello.component.ts, and placed the template code in the app.component.html.

To see a working example, please refer to my sample on stackblitz by following this link: working sample on stackblitz. This sample allows you to add (by clicking 'Add') and remove (by clicking 'x') products from your FormArray.

Example of form value with two products:

{
  name: 'John',
  surName: 'Doe',
  opticianOrders: {
    orderDescription: '1234',
    products: [
      { name: 'Cookies', manufacturerName: '' },
      { name: 'More Cookies', manufacturerName: '' }
    ]
  }
}

In Typescript, when accessing

this.orderForm.controls.opticianOrders
, it is an AbstractControl which does not have a controls property. You will need to cast it to a FormGroup first. Similarly, for products, you must cast it to a FormArray.

removeProduct(i: number){
  const aFormGroup = this.orderForm.controls.opticianOrders as FormGroup;
  const aFormArray = aFormGroup.controls.products as FormArray;
  aFormArray.removeAt(i);    
}

Answer №3

Give this a shot

orderForm.controls.opticianOrders.controllers

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

Accordion content from MUI gets cut off by bookmark bar when expanded

How can I prevent an MUI accordion in an appBar from moving up and hiding behind the browser's bookmark bar when expanded? const useStyles = makeStyles((theme)) => { appBar: { borderBottom: "2px solid #D2D2D2", backgro ...

Error: React-Redux/Jest Invariant Violation - The "store" could not be located

I have a test file set up that is similar to the one used by create-react-app. Here is my App.test.js file: App.test.js import React from 'react'; import ReactDOM from 'react-dom'; import { App } from './App'; it('rend ...

Compiling TypeScript files with an incorrect path when importing, appending "index" at the end of the @angular/material library

I'm currently working on creating a library to collect and distribute a series of Angular components across various projects, with a dependency on angular/material2. My objective is to eventually publish it on npm. However, I've encountered an i ...

The API endpoint returns a 404 not found error on NextJS 14 in the production environment, while it functions correctly on the local

I am in the process of creating a small website using NEXT JS 14. On my website, there is a contact us page that I have been working on. In the GetInTouch.tsx file, I have the following code: <Formik initialValues={{ ...

Tips on how to retrieve the current value of a scope variable in jQuery

When making a $http request to save data on the server and receiving a json response, I want to show an Android-style message using Toast for either success or failure based on the response. Initially, I set a scope variable to false $scope.showSuccessToa ...

Trouble accessing selected value from drop down list in ASP.NET codebehind following modification of Javascript attribute

Does anyone know why ASP.NET code behind cannot retrieve the selected value? After investigating, I discovered that the issue lies within the JavaScript function; Specifically in the set attribute section: process.options[process.selectedIndex].setAttrib ...

Find a match by comparing a field only with arrays that contain at least one element

I'm currently working on a filtering system. Let me show you a simple version of my Order model: { merchant: <Reference to the merchant>, date: <Date Object> name: "Some order name", ingredients: [{ ingred ...

jQuery is not updating the div as expected

While typing out this question, I'm hoping to uncover a solution that has eluded me so far. However, just in case... About a year ago, I successfully implemented something similar on another website and went through the code meticulously. Surprisingl ...

ClassNames Central - showcasing a variety of class names for interpolation variables

Seeking guidance on creating a conditional statement to set the className for a div element. The conditional is functioning correctly, and I can see the className being assigned properly in the developer console. However, I am struggling to return it as ...

Design a div in the shape of a trapezium with clipped overflow

Is there a way to create a trapezium using CSS/Html/Canvas? I have attempted to do so, but my current method is messy and not practical. div { width:0; margin-left:-1000px; height:100px; border-right:1000px solid lightblue; border-top:60px solid tra ...

Sharing a unique JSON message on Slack via a webhook

Is there a way to send a custom JSON message with indentation using a Slack webhook in a Node.js app? var Slack = require('slack-node'); var JsonMessage = process.argv[2]; webhookUri = "https://hooks.slack.com/services/XXXX/xxxx/xxxxxxxx"; sla ...

Enhancing an array item with Vuex

Is there a way to change an object within an array using Vuex? I attempted the following approach, but it was unsuccessful: const state = { categories: [] }; // mutations: [mutationType.UPDATE_CATEGORY] (state, id, category) { const record = state. ...

Forced line break at particular point in text

I would love to implement a line break right before the "+" character, either using css styling or through a different method. Is this task achievable? #myDiv{ width: 80% } #myP{ c ...

I am looking to have the first option in the dropdown menu appear as a placeholder text

In my form, I have two phone number fields - one for mobile phone and the other for home phone. I need to make only one of them mandatory. Can someone please advise me on how to accomplish this? ...

What is the best way to iterate through all class properties that are specified using class-validator?

I have a class defined using the class-validator package. class Shape { @IsString() value?: string @IsString() id?: string } I am trying to find a way to retrieve the properties and types specified in this class. Is there a method to do s ...

Browse through TypeScript objects using a browser tool

In my current workflow with VS 2015, I rely on the "Object browser" window for C# to easily navigate types in assemblies and namespaces, method overloads, and more. Is there a similar tool available for TypeScript that can browse TypeScript definition fil ...

Redirecting to new page after submitting form using Ajax

I'm having some trouble with my HTML form submission using JQuery and AJAX. After successfully submitting the form, I want to display a modal and then redirect to another page based on certain conditions. I've written the logic in my JS file wh ...

Exploring through a table using JavaScript

I am struggling to search a table within my HTML for a specific term. The code I have works to an extent, but it does not account for alternatives such as searching for "what is that" when I type in "What is that". Additionally, I need the script to ignor ...

The Angular2 app and NodeJs in the Docker container are unresponsive

After creating a new Angular2 app using angular-cli and running it in Docker, I encountered an issue where I could not connect to it from localhost. First, I initialized the app on my local machine: ng new project && cd project && "put m ...

Best practices for transferring objects between components while navigating routes in Angular

I need some advice on handling a specific component in my project. Here is the code snippet: <div class="animal"> <a routerLink="/animal/{{animal.id}}"> <div> ... </div> </a> </div> This component receives ...