Using Angular Material's MatChipList with a Dynamic FormArray: Step-by-step Guide

StackBlitz

My FormArray (variants) is structured like this:

this.productGroup = this.fb.group({
    name: '',
    variants: this.fb.array([
      this.fb.group({
        type: '',
        options: ''
      })
    ])
})

In my code, I am utilizing MatChips to store a string array that needs to be passed to options:

<div formArrayName="variants" *ngFor="let item of productGroup.controls['variants'].controls; let i = index;">
  <div [formGroupName]="i">
    <div class="row">
      <mat-form-field class="col-12">
        <input formControlName="type">
      </mat-form-field>
    </div>
    <div class="row">
      <mat-form-field class="col-12">
        <mat-chip-list #chipList>
          <mat-chip *ngFor="let opt of typesOptions" [selectable]="true"
                    [removable]="true" (removed)="removeOpt(opt)">
            {{opt}}
            <mat-icon matChipRemove>cancel</mat-icon>
          </mat-chip>
          <input placeholder="Set of options for this Type"
                  formControlName="options"
                  [matChipInputFor]="chipList"
                  [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
                  [matChipInputAddOnBlur]="true"
                  (matChipInputTokenEnd)="addOpt($event)">
        </mat-chip-list>
      </mat-form-field>
    </div>
  </div>
  <div class="row">
    <a href="javascript:" (click)="addItem()"> Add Variants </a>
    <a href="javascript:" (click)="removeItem(i)" *ngIf="i > 0"> Remove Variants </a>
  </div>
</div>

The methods used in this process are as follows:

// Dynamic Methods
  addItem(): void {
    this.variantsArray = this.productGroup.get('variants') as FormArray;
    this.variantsArray.push(this.fb.group({
      type: '',
      options: ''
    }));
  }
  removeItem(index: number) {
    this.variantsArray.removeAt(index);
  }

// MatChip Methods
  addOpt(item: number, event: MatChipInputEvent): void {
    const input = event.input;
    const value = event.value;
    // Add our fruit
    if ((value || '').trim()) {
      this.typesOptions.push(value.trim());
    }
    // Reset the input value
    if (input) {
      input.value = '';
    }
  }
  removeOpt(opt: string): void {
    const index = this.typesOptions.indexOf(opt);
    if (index >= 0) {
      this.typesOptions.splice(index, 1);
    }

Although dynamic fields can be successfully added to the variants formArray, the issue lies in having a static MatChipList for all dynamic fields. Is there a way to dynamically generate MatChipList for each field? Perhaps by changing

<mat-chip-list #chipList+i>
or something similar.

EDIT: StackBlitz

Answer №1

It seems that the issue may not lie with the dom reference variable #chiplist. The matChipList appears to be connected to the typesOptions array, which is a single array in your code. Each matChipList component is linked to this same array, causing them all to share the same data. To resolve this, consider creating an array of arrays for typesOptions. This way, each matChipList will have its own separate array. When adding an item, remember to also push a new sub-array to typesOptions (and remove it when removing an item).

While I haven't implemented this solution myself, this suggestion is based on a quick examination of your code.

I did create a solution based on James's stackblitz example.

https://stackblitz.com/edit/angular-3od6rd-jkidxf

Note: I didn't delve deep into how the delete variant functionality works. Ideally, using a key/value pair to track variant options might be better than relying solely on the outer loop index.

Here is some sample code from the stackblitz:

/* Sample implementation goes here */

Answer №2

Consider creating a new component for formGroup and passing the formGroup input to it instead of using formGroupName.

<div formArrayName="variants" *ngFor="let item of productGroup.controls['variants'].controls; let i = index;">
  <variant [varientGroup]="item"><varient>
  <div class="row">
    <a href="javascript:" (click)="addItem()"> Add Variants </a>
    <a href="javascript:" (click)="removeItem(i)" *ngIf="i > 0"> Remove Variants </a>
  </div>
</div>

varient component.html

<div [formGroup]="varientGroup">
 <div class="row">
    <mat-form-field class="col-12">
      <input formControlName="type">
    </mat-form-field>
 </div>
 <div class="row">
    <mat-form-field class="col-12">
      <mat-chip-list #chipList>
        <mat-chip *ngFor="let opt of typesOptions" [selectable]="true"
                [removable]="true" (removed)="removeOpt(opt)">
          {{opt}}
          <mat-icon matChipRemove>cancel</mat-icon>
        </mat-chip>
        <input placeholder="Conjunto de opções deste Tipo"
              [matChipInputFor]="chipList"
              [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
              [matChipInputAddOnBlur]="true"
              (matChipInputTokenEnd)="addOpt($event)">
      </mat-chip-list>
    </mat-form-field>
  </div>
</div>

in varient.component.ts

@Input()varientGroup: FormGroup

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

In Vue JS, assign a boolean value to a variable based on the screen width

Currently, I am working on a responsive navigation menu. I have completed the mobile view, but now I face an issue for the desktop view. I want to hide the button that toggles the mobile navbar and only display the navbar. However, the navbar is linked to ...

Ways to set the base URL on the development server for a call to react-scripts start

I am facing an issue with configuring my primary PHP server to run the ReactJS dev server created using yarn start within a directory named /reactive on the PHP site (using nginx proxy_pass). The problem is that I cannot set the root of the node server to ...

Encountering a Lint error stating "Expected 2 arguments, but received 1" during the testing of the window.scrollBy() function in Jasmine following an Angular

During the upgrade to Angular 11, several other packages, such as jasmine-core, were also upgraded. This caused lint issues when running the 'npm run test' command due to stricter type definitions. One specific issue involves the window.scrollBy ...

Is it possible to add custom attributes to Angular routing configuration in order to interact with them in a component?

I have created a routing config example below. However, I am looking to dynamically add and remove classes in my HTML based on a specific route within a few of my components. Is there a way to include additional 'attributes' (similar to component ...

Enclose the quotes within an array in JSON format

I've been attempting to construct a JSON array, but I'm encountering some issues: var sample = '{"sections":[{"id":"example1", "title":"Data Analysis", "command":"openSection("analysis")"}]}'; I've identified the problem with the ...

Slowly reveal a portion of the picture

I have created a slider that changes pictures when the next button is clicked. Everything works perfectly on localhost, but on the server, some images fade in halfway instead of fully displaying. There are 39 pictures in total for the slider, each with a s ...

The SQL statement functions correctly in the MYSQL command line but encounters issues when executed within a NODE.JS application

When running the following SQL as the root user, everything works fine: sudo mysql -u root -p DROP DATABASE IF EXISTS bugtracker; CREATE DATABASE bugtracker; USE bugtracker; However, when executing the node.js code, an error is encountered: Error: There ...

Troubleshooting a config file problem related to prefixes

While exploring discord.js at , I encountered a problem that has me stuck. Index.js const Discord = require('discord.js'); const { prefix, token } = require('./config.json'); const client = new Discord.Client(); client.on('ready& ...

Processing an Ajax request sent from Internet Explorer using PHP

Creating a real-time schedule for a school has been my latest project. While testing the system, I encountered an issue that seems to be exclusive to Internet Explorer. I am using IE version 11 and the character set UTF-8. When attempting to send data abo ...

The e2e Protractor test is unable to identify the Angular component within a complex Angular router with multiple layers

I am currently working on an Angular application and I need to set up end-to-end testing for this project. Angular Package: 4.6.6 Protractor: 5.3.0 In addition, my project includes a multi-layer router that wraps the router-outlet into another component ...

Modify the background of a DIV upon clicking it

Is there a way to achieve the following scenario: I have multiple DIVS <div style="background-color: black;">text1</div> <div style="background-color: black;">text2</div> <div style="background-color: bl ...

Verify if the input text field meets the minimum value requirement upon submission

I have a basic HTML form on my website... After collecting the user input, I convert it into JSON data (intending to send it elsewhere later on. Currently, just using console.log for testing). Once the user fills out the form and clicks submit, I want to ...

When attempting to display the title of a route in an Angular header component, it consistently results in undefined

I am facing a seemingly simple issue. I have a header component that needs to display the title of the currently active route. To achieve this, I am injecting the ActivatedRoute into the header component and attempting to display the route title using the ...

Utilize varied CSS files for distinct sections within two different subdirectories

In my Angular application, I have created two sub-roots under the main app root: websiteMaster and systemMaster. I want the CSS files of the website to be loaded only when a user is logged in, and the CSS files of the systems to be loaded only when a us ...

The TypeScript error reads: "An element is implicitly assigned the 'any' type because an expression of type 'any' cannot be used to index a specific type."

[Hey there!][1] Encountering this TypeScript error message: { "Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{ 0: { image: string; title: string; text: string; }; 1: { ...

When interacting with input boxes in Ionic webview for Android, the entire section will shift upward

I am currently working with Ionic version 3.10 and have included the following login HTML code: <ion-content> <ion-grid> <ion-row> <ion-col col-12> <ion-list> <ion-ca ...

Can variables in JavaScript clash with input names in HTML?

I have the following code in an HTML file: <input type="..." name="myInput1" /> Then, in a JS file associated with this HTML file, I declare a variable to store the value of that input after it loses focus: var myInput1; Should I be concerned abo ...

How come the values in my object remain inaccessible even after assigning values to it with navigator.geolocation.getCurrentPosition() and returning it?

After successfully assigning values to my pos object within the whereAmI function using navigator.geolocation.getCurrentPosition(), I can confirm that lat and lng are present inside the object. However, attempting to access these properties later on resu ...

jQuery template does not respond to input text when a click event is enabled on an iPhone device

Below is a jQuery template I have: <div class="parent-class"> <div class="sub-class"> <div clas="sub-input-class"> <input type="text" /> </div> </div> </div> Recently, I ...

How to center text in MatButtonToggle within angular material?

I'm facing a challenge in customizing the MatButtonToggle as I am having issues with centering the label: https://i.sstatic.net/LVzrm.png Here is the template for reference: <mat-button-toggle-group name="condition" aria-label="Condition"> ...