The user interface is not being refreshed in the select box after removing control from the reactive form

Within my project, I am utilizing "@angular/cli": "1.2.6", "@angular/core": "^4.0.0"

Objective
My goal is to create a dynamic form for a product that includes feature inputs. When the user clicks the "add feature" button, a new feature column with a select box for "type" should appear. If the user then clicks the "remove" button within a feature column, that specific feature column should be deleted from the form.

Issue Faced
The problem arises when attempting to remove a feature column between the first and last columns. While the formControl updates correctly with the desired value, the UI displays an issue where the removed feature's select box values pass onto the next upcoming feature column.

Expected Outcome
Upon removing a target feature column, the UI of the subsequent feature column should move up accordingly with the correct value.

Example
1. I attempt to remove the second feature column. https://i.stack.imgur.com/fN3i1.png 2. The formControl successfully removes the second feature. Although the second feature column in UI is effectively deleted, the third feature column moves up and fills the space with the previously removed feature's select box value. https://i.stack.imgur.com/MYMKM.png

Below is a snippet of my code:

product-form.component.ts

formDOM;
features = [];
featureTypes = [
  { id: "pros", name: "pros" },
  { id: "cons", name: "cons" }];

ngOnInit() {
  this.formDOM = this.formBuilder.group({
    // Other fields...
    feature: this.formBuilder.array([])
  });
}

patchSingleFeature(feature: object): FormGroup {
  let returnObject = this.formBuilder.group({
    type: (feature && feature['type'])
    // Additional fields....
  });

  this.features.push("feature");
  return returnObject;
}

addFeature(): void {
  let featureControl = <FormArray>this.formDOM.controls['feature'];
  featureControl.push(this.patchSingleFeature(new Feature()));
}

removeFeature(x: number): void {
  let numberOfFeature = this.features.length;
  let featureControl = <FormArray>this.formDOM.controls['feature'];
  featureControl.controls.splice(x, 1);
  this.features.splice(x, 1);
}

product-form.component.html

<div class="form" [formGroup]="formDOM">
  <div class="col-xs-12">Features</div>
  <div *ngFor="let feature of features; let x = index; ">
    <feature-input [x]="x" [featureTypes]="featureTypes" [form]="formDOM" (clickEvent)="removeFeature($event)"></feature-input>
  </div>
  <button (click)="addFeature()">Add Feature</button>
</div>

feature-input.component.html

<div class="feature-input" [formGroup]="form">
  <div formArrayName="feature">
    <div [formGroupName]="x">
      <select formControlName="type">
        <option value="" disabled>-- Select Feature Type --</option>
        <option *ngFor="let type of featureTypes" [value]="type.id">{{ type.name }}</option>
      </select>
    </div>
  </div>
</div>

feature-input.component.ts

@Input() form: FormGroup;
@Input() featureTypes: Array<object>;
@Input() x: number;
@Output() clickEvent new EventEmitter<number>();

removeFeature(x) { this.clickEvent.emit(x); }

Answer №1

If I were to modify the code provided, I would suggest sending the entire form array to the child component and iterating through it there. Since objects are mutable in JavaScript, there is no need for an EventEmitter; the deletion can be done in the child component without emitting anything to the parent.

In this scenario, where there is no nested form group, you can simply pass the complete form to the child component like this:

<feature-input [featureTypes]="featureTypes"></feature-input>

After adjusting the input fields accordingly, the template for your child component could resemble the following:

  <div class="feature-input" [formArray]="featureTypes">
    <div *ngFor="let ctrl of featureTypes.controls; let i = index" [formGroupName]="i">
      <select formControlName="type">
      <option value="" disabled>-- Select Feature Type --</option>
      <option *ngFor="let type of featureTypes" [value]="type.id">{{ type.name }}</option>
      </select>
      <button (click)="removeFeature(i)">Remove</button>    
    </div>
  </div>

Within the removeFeature function, all that's required is passing the index of the form group and utilizing the removeAt function, which is specific to form arrays:

removeFeature(index) {
  this.formGroup.get('feature').removeAt(index)
}

Answer №2

The recommended method is to use the removeAt function directly on the FormArray with the index parameter.

(<FormArray>this.myForm.controls['items']).removeAt(index);

If you choose to use splice on the form controls instead, make sure to call updateValueAndValidity on each item to update the array properly.

this.formControls.splice(index, 1)

this.formControls.map(data => {data[controls].value.updateValueAndValidity({ onlySelf: false, emitEvent: false });});

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

Angular Github Deployment Issue: Page malfunctioning as a result of strict-origin-when-cross-origin restriction

I am currently learning Angular and attempting to deploy it on Github Pages. However, I have encountered an issue where the app is not functioning properly. After inspecting the page, I discovered a CORS origin error when trying to access certain resource ...

Ways to change a value into int8, int16, int32, uint8, uint16, or uint32

In TypeScript, the number variable is floating point by default. However, there are situations where it's necessary to restrict the variable to a specific size or type similar to other programming languages. For instance, types like int8, int16, int32 ...

Why does using `withCredentials: true` and including a `body` in the request cause a CORS error in Angular HttpClient?

My objective is to make a request to a Cloud Function, receive a response with a Set-Cookie header, and have the browser store the cookie. The issue arises when the response containing a Set-Cookie header is ignored without the presence of withCredentials ...

Error in Angular: Trying to access the property 'id' of an undefined value

I am facing an issue with a div tag in my HTML file. The code snippet looks like this: <div *ngIf="chat.asReceiver.id != user?.id; else otherParty"> Unfortunately, it always returns the following error: ERROR TypeError: Cannot read propert ...

Improving the utilization of services in Angular

I have a DatesService that handles date manipulation. Additionally, I have two services that require date manipulation - EventsService and CalendarService. The CalendarService utilizes the EventsService. My query is: what would be more efficient (in terms ...

Implementing captivating animations in Angular2 for an *ngFor loop can add a unique

Exploring the Angular2 documentation has left me stumped on how to incorporate delays into animations. My goal is to achieve a similar effect to this jQuery plunkr example. I am utilizing Angular2's animation capabilities in generating these "bars" w ...

Translate Firestore value updates into a TypeScript object

Here are the interfaces I'm working with: interface Item { data: string } interface Test { item: Item url: string } In Firestore, my data is stored in the following format: Collection Tests id: { item: { data: " ...

A step-by-step guide on creating a Decorator using the TypeScript compile API

How can I create a custom class in TypeScript with multiple 'class-validator' decorators to ensure the property types are correct? I am considering using `ts.factory.createDecorator`, but I'm unsure how to obtain a `ts.Expression` for it. ...

Enhance Express Middleware with Typescript for advanced functionality

Currently in the process of developing a REST API using Express and Typescript, I am encountering difficulties when trying to extend the Request/Response objects of Express. Although my IDE shows no errors, Typescript throws TS2339 Errors during compilati ...

Having trouble with installing angular-cli

angular-cli unexpectedly quits while trying installing: (myapp)vagrant@myapp-local:/vagrant$ sudo npm install -g angular-cli npm WARN deprecated <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="caadb8aba9afacbfa6e7acb98afbe4f8e4 ...

Angular button for opening or closing the menu that redirects the page to a localhost URL

I have implemented a template from the link below into my project. So far, everything has been working fine, but recently I noticed that the menu open/close button is malfunctioning. Whenever I click on the close button while on any page (for example, http ...

Node.js/Express API Endpoint Ceases Functioning

In my Angular/Express.js app, there is a post method within my api.service.ts file: post(data: any, endpointUrl: string): Observable<T> { console.log("REACHED POST METHOD") return this.http.post<T>(`${this.apiUrl}/${endpoint ...

Using Sass variables within Angular2 components

In my project, I leverage Angular2 and angular-cli. Within the global style.scss file, I have defined several Sass variables. How can I retrieve these variables within my custom components (component.scss)? Should I perhaps import a separate file contain ...

Angular 5: How to Calculate the Sum of Two Numbers and Handle NaN Output

I have encountered an issue where I am trying to multiply two numbers and add another number, but the output is displaying as NaN. How can I troubleshoot and solve this problem? Below is the code snippet: medicines = [new Medicine()]; this.sum = 0;// su ...

A JavaScript function written without the use of curly braces

What makes a function good is how it is declared: export declare class SOMETHING implements OnDestroy { sayHello() { // some code to say hello } } However, while exploring the node_modules (specifically in angular material), I stumbled up ...

How can I use "Lite-Server" with NPM start to showcase my index.cshtml file on the browser?

Currently, I am trying to navigate the world of Visual Studio Code and figure out how to run/compile my project. Starting a new project in Visual Studio was simple enough, but now that I'm working with Visual Studio Code, I find myself struggling to s ...

Is there Polyfill Compatibility for Custom Elements in Angular 9?

When it comes to polyfilling support for custom elements created with Angular, there are various recommendations available. This demo demonstrates that adding the following polyfill in polyfills.ts works: import '@webcomponents/webcomponentsjs/custo ...

Is there a way to emphasize certain words with angular?

In order to convert all words starting with "@" into an <a> (html link) element, I am looking for a solution using Angular 8 and considering the use of a custom pipe. For example, if the phrase is "Hello, @test" -> "Hello, <a href="...&qu ...

Steps for developing a Global service in Angular

I've come across many discussions on global angular services, but none seem to truly embody the concept of "global". I'm curious if it's feasible to develop a service that doesn't require importing everywhere it's used (i.e. truly ...

"Although the NextJS client-side data is present, it seems to be struggling to

I am facing an issue while trying to display fetched data on a blog page. Although the data is successfully retrieved client-side and I can view it using console.log(), it does not appear on the page and the elements remain empty. I am completely puzzled. ...