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

Steps for integrating a valid SSL certificate into a Reactjs application

After completing my ReactJS app for my website, I am now ready to launch it in production mode. The only hurdle I face is getting it to work under https mode. This app was developed using create-react-app in a local environment and has since been deployed ...

Issue with Angular 17 button click functionality not functioning as expected

Having trouble with a button that should trigger the function fun(). Here's the code snippet I'm using. In my TS file: fun(): void { this.test = 'You are my hero!'; alert('hello') } Here is the respective HTML: &l ...

Using TypeScript to define a constant array as a type

I've hit a roadblock in my typescript project while trying to define a suitable type. Here's the setup: Within my project, I have the following constant: export const PROPERTYOPTIONS = [ { value: "tag", label: "Tag" }, { ...

Ways to conceal a fabric button

Is there a way to hide my Material button properly? The button appears grey and works fine: <button mat-raised-button class="mat-primary" (click)="deleteClick()" [disabled]="data.createMode"> <mat-icon>delete_forever</mat-icon>DELET ...

Having trouble with Angular 4 data display? See how to fix a simple example that's

I can't seem to display my data correctly in Angular. When I try, all I get are empty bullet points and an error message that says "Cannot read property of 0 undefined." Even though the code appears to be correct, it's not functioning as expected ...

Passing a click event from a route-outlet to app.component in Angular 2

I'm brand new to using Angular 2 and developing an application app.routing.ts const appRoutes: Routes = [ {path:'', component:HomeComponent}, { path: 'login', component: LoginComponent }, { path: 'register&ap ...

Calculate the total by subtracting values, then store and send the data in

Help needed with adding negative numbers in an array. When trying to add or subtract, no value is displayed. The problem seems to arise when using array methods. I am new to arrays, could someone please point out where my code is incorrect? Here is my demo ...

Update dynamically generated CSS automatically

Is there a way to dynamically change the CSS? The problem I'm facing is that the CSS is generated by the framework itself, making it impossible for me to declare or modify it. Here's the scenario at runtime: I am looking to target the swiper-pa ...

Angular's ngbdatepicker encountering RangeError: Call stack size has surpassed maximum limit

Below is the code snippet used in my angular component template. <input type="text" (click)="dp.toggle()" ngbDatepicker #dp="ngbDatepicker" id="myDatePicker" autocomplete="off" placeholder= ...

Bringing in the MVC model class for an ASP.NET Core MVC application paired with an Angular 2 application

Currently, I am developing a sample Angular 2 application alongside ASP.NET Core MVC. I am curious if it is feasible to import a model class (let's say product.cs) that has been created in the "Models" folder directly into the Angular 2 application i ...

The 'localstorage' object is not defined in NextJS 14, make sure to use 'client' in server-side execution

While setting up the Tailwind context for a Next.js 14 website, I encountered an issue with configuring a global theme for my project. Despite creating the ThemeContext and adding 'use client' at the beginning of the file, it still caused an erro ...

Creating an Angular Accordion/Zippy Component: A Step-by-Step Guide

I am currently tackling a project involving the presentation of a list of grievances in a zippy/accordion format. The data set I work with is an array of key-value pairs found in my component.ts file, shown below: isExpanded: boolean; complaints: any[] = ...

unable to verify identity through a web browser

When I try to launch my Android application built with Ionic 2 on my smartphone, I encounter this error: If you need a tutorial for the application, check out: https://medium.com/appseed-io/third-party-authentication-for-your-ionic-2-mobile-app-9fdd43169d ...

When attempting to upload an image in Angular 9, I am receiving an empty object as a result

I am encountering issues with my formData resulting in an empty object or a bad request error when trying to post a file upload to the server. The problem seems to originate from the service where FormData is being created, as it sends the formData over as ...

What could be causing the error in the console when I try to declare datetime in Ionic?

I am just starting out with Ionic and Angular, but I seem to have hit a roadblock. The compiler is throwing an error that says: node_modules_ionic_core_dist_esm_ion-app_8_entry_js.js:2 TypeError: Cannot destructure property 'month' of '(0 , ...

Manipulating the DOM within an Angular application

What is the best way to perform DOM manipulation in Angular without using jQuery? Here is an example of code using jQuery: $(".next-step").click(function (e) { var $active = $('.wizard .nav-tabs li.active'); $active.next().removeClass(& ...

Warning: Ionic 4 has encountered error code ITMS-90809, indicating the use of deprecated API. Apple has announced they will no longer accept app submissions that utilize

Greetings from the Apple team, It has come to our attention that there are certain issues with your recent app delivery for "Project 66" version 0.0.9. Your delivery was successful, however, we advise you to address the following problem in your upcoming ...

Accessing React.FC in Another File with TypeScript - A Step-by-Step Guide

code - const Exne: React.FC <IProps> = ({x}) => { console.log('input', x); const [getx, assignx] = useState(x); console.log(getx, assignx); return(getx) }; Could you please provide instructions on how to acc ...

`Incorporate concurrent network requests in React for improved performance`

I am looking to fetch time-series data from a rest service, and currently my implementation looks like this async function getTimeSeriesQuery(i) { // Demonstrating the usage of gql appollo.query(getChunkQueryOptions(i)) } var promises = [] for(var i ...

Angular2 Navigation: Redirecting to a dynamically constructed route

To start, I need to automatically redirect to today's date as the default. Below is the routing configuration I currently have set up: import { CALENDAR_ROUTE } from './_methods/utils'; export const appRoutes: Routes = [ { path: Cal ...