77

Say i have the following object array, lets name it itemArray;

{
  "totalItems": 2,
  "items": [
    {
      "id": 1,
      "name": "foo"

    },
    {
      "id": 2,
      "name": "bar"
    },
    ]
}

And i have a subscription that returns the updated result of only id 2. How would i update the object array without looping through the entire array?

What i would like is something like the example below;

updateUser(user){
    this.myservice.getUpdate(user.id)
    .subscribe(newitem => {
      this.updateArray(newitem);
    });
}

  updateArray(newitem){
    this.itemArray.items[newitem.id].name = newitem.name
  }

or even better, replacing the entire object;

  updateArray(newitem){
    this.itemArray.items[newitem.id] = newitem
  }

This example however updates the array based on the index of the array. So how do i instead update based on newitem.id?

Template requested in comment:

<tr *ngFor="let u of itemsArray.items; let i = index">
  <td>{{ u.id }}</td>
  <td>{{ u.name }}</td>
  <td>
    <input type="checkbox" checked="u.accepted" [(ngModel)]="itemsArray.items[i].accepted" (ngModelChange)="updateUser(u)">
    <label for="singleCheckbox-{{i}}"></label>
  </td>
</tr>
6
  • Are you using this object for rendering the output from a loop on this itemArray (by id of 2)? What I'm getting at, is could you just write a custom pipe for this filter? Commented May 22, 2017 at 19:26
  • "This example however updates the array based on the index of the array. So how do i instead update based on newitem.id?". Why don't you want to update an item from an index? It will certainly be faster than looping the array searching for a specific id... Commented May 22, 2017 at 19:26
  • updating from index would be fine. How would i find the right index? Now the index does not correspond with the newitem.id Commented May 22, 2017 at 19:27
  • although this is a response from the back-end it does not have the index. Could i lookup the index based on the newitem.id in this.itemArray.items? Commented May 22, 2017 at 19:30
  • Can you include your template in question? Commented May 22, 2017 at 19:41

11 Answers 11

118

Update:

  showUpdatedItem(newItem){
    let indexToUpdate = this.itemArray.items.findIndex(item => item.id === newItem.id);
    this.itemArray.items[indexToUpdate] = newItem;

   // some angular libraries require breaking the array reference
   // to pick up the update in the array and trigger change detection.
   // In that case, you can do following

   this.itemArray.items = Object.assign([], this.itemArray.items);
  }

Stackblitz Demo

Original Answer:

I have created this Plunker based on your example that updates the object equal to newItem.id

Here's the snippet of my functions:

showUpdatedItem(newItem){
    let updateItem = this.itemArray.items.find(this.findIndexToUpdate, newItem.id);

    let index = this.itemArray.items.indexOf(updateItem);

    this.itemArray.items[index] = newItem;

  }

  findIndexToUpdate(newItem) { 
        return newItem.id === this;
  }
Sign up to request clarification or add additional context in comments.

5 Comments

Minor typo at the end of the second line. )); should be );
excellent! only part i am confused on is how to refresh the array on the screen.
Correct answer, but could be shortened by using findIndex instead.
Thanks Nahal. I used your code here - stackblitz.com/edit/angular-item-edit-jk7b9k
Back then this was probably the way to go. But Angular digest prefers a new array to make it easier for them to see that it was updated. I was looking for the correct splice syntax. But splice isn't the answer either. The best way to do this is by using map this.itemArray = this.itemArray.map((entry) => entry.id === newItem.id ? newItem : entry); (You can write this multiline if you want to make it prettier)
44

Updating directly the item passed as argument should do the job, but I am maybe missing something here ?

updateItem(item){
  this.itemService.getUpdate(item.id)
  .subscribe(updatedItem => {
    item = updatedItem;
  });
}

EDIT : If you really have no choice but to loop through your entire array to update your item, use findIndex :

let itemIndex = this.items.findIndex(item => item.id == retrievedItem.id);
this.items[itemIndex] = retrievedItem;

Comments

35

Another approach could be:

let myList = [{id:'aaa1', name: 'aaa'}, {id:'bbb2', name: 'bbb'}, {id:'ccc3', name: 'ccc'}];
let itemUpdated = {id: 'aaa1', name: 'Another approach'};

myList.find(item => item.id == itemUpdated.id).name = itemUpdated.name;

Comments

9

You can try this also to replace existing object

toDoTaskList = [
                {id:'abcd', name:'test'},
                {id:'abcdc', name:'test'},
                {id:'abcdtr', name:'test'}
              ];

newRecordToUpdate = {id:'abcdc', name:'xyz'};
this.toDoTaskList.map((todo, i) => {
         if (todo.id == newRecordToUpdate .id){
            this.toDoTaskList[i] = updatedVal;
          }
        });

1 Comment

This is not the purpose of an array's map() function.
8

Try Array.ForEach() method.

itemArray.ForEach(item =>{
    if(item.id == newitem.id){
        item.name = newitem.name
    }
});

Comments

3

I would rather create a map

export class item{
    name: string; 
    id: string
}

let caches = new Map<string, item>();

and then you can simply

this.caches[newitem.id] = newitem; 

even

this.caches.set(newitem.id, newitem); 

array is so 1999. :)

1 Comment

I'm not sure but I believe a Map is not iterable in an *ngFor loop. I believe I had that issue before in Angular with maps.
3

You can update one item like.

let item = list.find(item => item.id === id);

if( item ) {

  item.name = 'other name';

} 

Comments

2
updateValue(data){    
     // retriving index from array
     let indexValue = this.items.indexOf(data);
    // changing specific element in array
     this.items[indexValue].isShow =  !this.items[indexValue].isShow;
}

Comments

0

You can use for loop to find your element and update it:

updateItem(newItem){
  for (let i = 0; i < this.itemsArray.length; i++) {
      if(this.itemsArray[i].id == newItem.id){
        this.users[i] = newItem;
      }
    }
}

1 Comment

You can simply use this.users.push( itemsArray.find( el => el.id == newItem.id ) )
0

In angular/typescript we can avoid mutation of the objects in the array.

An example using your item arr as a BehaviorSubject:

// you can initialize the items$ with the default array
this.items$ = new BehaviorSubject<any>([user1, user2, ...])

updateUser(user){
   this.myservice.getUpdate(user.id).subscribe(newitem => {

     // remove old item
     const items = this.items$.value.filter((item) => item.id !== newitem.id);

     // add a the newItem and broadcast a new table
     this.items$.next([...items, newItem])
   });
}

And in the template you can subscribe on the items$

<tr *ngFor="let u of items$ | async; let i = index">
   <td>{{ u.id }}</td>
   <td>{{ u.name }}</td>
   <td>
        <input type="checkbox" checked="u.accepted" (click)="updateUser(u)">
        <label for="singleCheckbox-{{i}}"></label>
   </td>
</tr>

Comments

0

Find that item index and then update it.

public editData(key: any, Value: any) {
    const index = this.EmpData.findIndex((item) => item.id === key.id);

    console.log(index);
    
    if (index !== -1) {
        Value.value.id = key.id;
        this.EmpData[index] = Value.value;
        const jsonData = JSON.stringify(this.EmpData);
        localStorage.setItem('EmpData', jsonData);
    } 
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.