Suraj Gupta
Suraj Gupta

Reputation: 465

How to update filtered array after deleting item in angular

I am trying to delete an item from filtered list but it is not getting updated.

I am able to delete item without filtering.

html

<div class="search-hero">
      <input
        class="form-control"
        type="text"
        name="search"
        [(ngModel)]="searchText"
        autocomplete="off"
        placeholder="&#61442;  Start searching for a hero by id or name or country"
      />
    </div>
    <table class="table table-striped">
      <thead>
        <tr>
          <th>Id</th>
          <th>Hero Name</th>
          <th>Country</th>
          <th>Delete</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let hero of heroes | filter: searchText; let index = index">
          <td>{{ hero.id }}</td>
          <td>{{ hero.name }}</td>
          <td>{{ hero.country }}</td>
          <td (click)="onDelete(index)"><button>Delete</button></td>
        </tr>
      </tbody>
    </table>

Ts file:

searchText;
  heroes = [
    { id: 11, name: 'Mr. Nice', country: 'India' },
    { id: 12, name: 'Narco', country: 'USA' },
    { id: 13, name: 'Bombasto', country: 'UK' },
    { id: 14, name: 'Celeritas', country: 'Canada' },
    { id: 15, name: 'Magneta', country: 'Russia' },
    { id: 16, name: 'RubberMan', country: 'China' },
    { id: 17, name: 'Dynama', country: 'Germany' },
    { id: 18, name: 'Dr IQ', country: 'Hong Kong' },
    { id: 19, name: 'Magma', country: 'South Africa' },
    { id: 20, name: 'Tornado', country: 'Sri Lanka' },
  ];

  onDelete(index) {
    this.heroes.splice(index, 1);
    console.log('deleted');
  }

UI is not updating only when something is searched in the searchbar.

Here is the reproducible example link

Thanks!

Upvotes: 0

Views: 866

Answers (4)

Waqas
Waqas

Reputation: 31

I have investigated the issue and found that whenever you are filtering the data it shows the data on 1st index and when you click the delete button it always delete the value on 0 index because the searched value was showing on index 0.

The solution is that you have to pass hero.id on clicking onDelete() function and pass the id in the parameter instead of index:

 <tbody>
    <tr *ngFor="let hero of heroes | filter: searchText; let index = index">
      <td>{{ hero.id }}</td>
      <td>{{ hero.name }}</td>
      <td>{{ hero.country }}</td>
      <td (click)="onDelete(hero.id)"><button>Delete</button></td>
    </tr>
  </tbody>

and then in ts file get the index base on id:

onDelete(id) {
let index = this.heroes.findIndex((x) => x.id == id);
this.heroes.splice(index, 1);
console.log('deleted');

}

It will solve the issue.

Upvotes: 1

Askirkela
Askirkela

Reputation: 1209

Angular change detection works by reference. By using splice on your array, the original reference (memory address) is not modified.

You would be best to do something like the following:

onDelete(id: number) {
  this.heroes = this.heroes.filter(hero => hero.id !== id);
}

This would trigger the change detection.

In addition to that, you could add a trackBy function to your *ngFor so that the dom for the array is not rewritten in full when only one item changes.

Upvotes: 1

馬料水碼農
馬料水碼農

Reputation: 171

The hero in the heros list is identified by a unique id, you can try to delete it by a unique id.

<div class="container text-center">
  <h1>{{ title }}</h1>
</div>
<div class="container">
  <div class="row">
    <div class="search-hero">
      <input
        class="form-control"
        type="text"
        name="search"
        [(ngModel)]="searchText"
        autocomplete="off"
        placeholder="&#61442;  Start searching for a hero by id or name or country"
      />
    </div>
    <table class="table table-striped">
      <thead>
        <tr>
          <th>Id</th>
          <th>Hero Name</th>
          <th>Country</th>
          <th>Delete</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let hero of heroes | filter: searchText; let index = index">
          <td>{{ hero.id }}</td>
          <td>{{ hero.name }}</td>
          <td>{{ hero.country }}</td>
          <td (click)="onDelete(hero.id)"><button>Delete</button></td>
        </tr>
      </tbody>
    </table>
  </div>
</div>
import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'Angular Search Using ng2-search-filter';
  searchText;
  heroes = [
    { id: 11, name: 'Mr. Nice', country: 'India' },
    { id: 12, name: 'Narco', country: 'USA' },
    { id: 13, name: 'Bombasto', country: 'UK' },
    { id: 14, name: 'Celeritas', country: 'Canada' },
    { id: 15, name: 'Magneta', country: 'Russia' },
    { id: 16, name: 'RubberMan', country: 'China' },
    { id: 17, name: 'Dynama', country: 'Germany' },
    { id: 18, name: 'Dr IQ', country: 'Hong Kong' },
    { id: 19, name: 'Magma', country: 'South Africa' },
    { id: 20, name: 'Tornado', country: 'Sri Lanka' },
  ];

  onDelete(heroId: number) {
    console.log(heroId);
    this.heroes = this.heroes.filter((hero) => hero.id !== heroId);
    console.log('deleted');
  }
}

Example here

Upvotes: 1

DonJuwe
DonJuwe

Reputation: 4563

The problem is that you use the current index as the parameter to delete the item. If you filter for "Germany" and try to delete this object, the index of this very object is then 0 instead of 6 and you delete the first item from the list ({ id: 11, name: 'Mr. Nice', country: 'India' }). A better approach is to use id to make sure you delete the right object from the array:

<tr *ngFor="let hero of heroes | filter: searchText">
    <td>{{ hero.id }}</td>
    <td>{{ hero.name }}</td>
    <td>{{ hero.country }}</td>
    <td (click)="onDelete(hero.id)"><button>Delete</button></td>
</tr>

Method:

onDelete(id) {
    this.heroes.splice(this.heroes.findIndex(h => h.id === id));
}

Upvotes: 1

Related Questions