16

I have an array of items that is displayed in a table using ng-repeat. When you click on an item, that item is pulled from the server and the table should then be updated with the updated item.

Function to get the updated item when clicking on an item in the table:

$scope.getUpdatedItem = function(item){
    itemService.getItem(item).then(
        function(updatedItem){
            item = updatedItem;
        },
        function(error){
            //Handle error
        }
    );
};

I'm displaying the items using:

<tr ng-repeat="item in myItems">

The problem: The item in the table is never updated.

What's the best way to update the item in the ng-repeat? Can i use "track by $index" in the ng-repeat for this? Or do I have to iterate over myItems to find the item I want to replace?

Update:

A possible solution is instead of using

item = updatedItem,

to use:

var index = $scope.myItems.indexOf(item);
$scope.myItems[index] = updateItem;

However, I feel that there should be a "cleaner" way of doing this.

5
  • What does itemService loook like? Commented Apr 29, 2015 at 14:49
  • it looks to me as though you need a variable in your controller that you can set from the data in updatedItem. When item = updatedItem runs, the scope of item is just that function. Commented Apr 29, 2015 at 14:52
  • Are there one or two tables? One with the list of items and one for the selected item? Commented Apr 29, 2015 at 14:54
  • There is only one table. Commented Apr 29, 2015 at 14:55
  • @ jcuenod Can you give a short example of what you mean, please. Commented Apr 29, 2015 at 15:00

4 Answers 4

15

There isn't a much cleaner way (then your update). As you noticed, when you change item in your callback function you change the local reference, and not the original item in the array.

You can improve this a bit by using the $index from the ng-repeat, instead of calculating it yourself:

<div ng-click="getUpdatedItem(item, $index)"> </div>

And in your controller:

$scope.getUpdatedItem = function(item, index){
    itemService.getItem(item).then(
    function(updatedItem){
        $scope.myItems[index] = updateItem;
    },
    function(error){
        //Handle error
    }
    );
};

You can also use angular.copy instead but it's much less efficient:

function(updatedItem){
    angular.copy(updateItem, item);
},
Sign up to request clarification or add additional context in comments.

1 Comment

Usage of $index should be avoided as it is ng-repeat index and not the actual array index of the scope object which is bound to the view; Sometimes this can go horribly wrong in case you are doing sorting or filtering for example you have an array bird[0] = parrot; bird[1] = crow ; if you filter for crow, your grid will result in $index = 0 but in reality, crow was at index 1.
2

If I understand your problem properly

could something like this work?

<!-- template code -->
<table>
    ...
    <tr ng-repeat="(index, item) in items">
        <td>{{item.name}}</td>
        <td>
             {{item.detail}}
             <button ng-if="!item.detail" ng-click="loadItem(index)">
        </td>
    </tr>
</table>

// Controller Code
$scope.items = [...]
$scope.loadItem = function(index){
    itemService.getItemDetail($scope.items[index]).then(function(itemDetail){
        $scope.items[index].detail = itemDetail;
    });
};

6 Comments

That might not work due to the scope of ng-repeat. It only watches for changes on the object itself and not its properties.
I think that when any $q promise is resolved, the whole scope is checked again recursively
I just set up a plunker. It does seem to work plnkr.co/edit/1O0XFkz5oH4UKRQt0jV1 . ng-repeat does not have a deep watch because it only needs to know when there are new elements, but the {{item.extra}} in the template does i guess.
Yeah I just created a jsFiddle to check it too and it looks fine (I should have checked this first sorry) - jsfiddle.net/neridum/cvdz9r16 Perhaps by using the index directly it helps to avoid the issue.
I found a good answer on the topic stackoverflow.com/questions/15646304/…
|
1

item may start as a reference to an item in your list, but when you say:

item = updatedItem;

You reseat that binding -- you are no longer referring to the item in the list, but to the disconnected one that was returned in your promise. Either you will need to modify the item, like so:

function(updatedItem){
  item.varA = updatedItem.varA
  item.varB = updatedItem.varB
  ...
}

Or, if it gets too hairy, you might consider an item array that looks more like this:

var items = [ 
  { data: item1 },
  { data: item2 },
  { data: item3 }
};

At which point your update function will look like this:

function(updatedItem){
    item.data = updatedItem;
},

Comments

1

I've just spent hours on this issue. I couldn't use the $index solution from @eladcon, as my ng-repeat also used a filter, to the index isn't correct if the rows/items are filtered.

I thought I would be able to just do this:

$filter('filter')($scope.rows, {id: 1})[0] = newItem;

but that doesn't work.

I ended up iterating the array until I found a match, and then using the $index from the iteration (not from the ng-repeat) to set the array item to the new item.

// i'm looking to replace/update where id = 1
angular.forEach($scope.rows, function(row, $index) {
  if (row.id === 1) {
    $scope.rows[$index] = newItem;
  }
})

See here: https://codepen.io/anon/pen/NpVwoq?editors=0011

1 Comment

Maybe not the prettiest, but I also spent hours on this and this is a good solution.

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.