Как заменить объект в массиве, который отображается с помощью ng-repeat?

У меня есть массив элементов, который отображается в таблице с помощью ng-repeat. Когда вы нажимаете на элемент, этот элемент извлекается с сервера, и затем таблица должна обновляться обновленным элементом.

Функция для получения обновленного элемента при нажатии на элемент в таблице:

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

Я показываю элементы, используя:

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

Проблема: элемент в таблице никогда не обновляется.

Какой лучший способ обновить элемент в ng-repeat? Могу ли я использовать "track by $index" в ng-repeat для этого? Или мне нужно перебирать myItems, чтобы найти элемент, который я хочу заменить?

Update:

Возможное решение вместо использования

item = updatedItem,

для использования:

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

Однако я чувствую, что должен быть "более чистый" способ сделать это.

Ответ 1

Существует не намного более чистый способ (тогда ваше обновление). Как вы заметили, при изменении item в вашей функции обратного вызова вы меняете локальную ссылку, а не исходный элемент в массиве.

Вы можете немного улучшить это, используя $index из ng-repeat, вместо того, чтобы вычислять его самостоятельно:

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

И в вашем контроллере:

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

Вместо этого вы можете использовать angular.copy, но он намного менее эффективен:

function(updatedItem){
    angular.copy(updateItem, item);
},

Ответ 2

Если я правильно понимаю вашу проблему

может что-то вроде этой работы?

<!-- 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;
    });
};

Ответ 3

item может начинаться как ссылка на элемент в вашем списке, но когда вы говорите:

item = updatedItem;

Вы переустановите это обязательство - вы больше не ссылаетесь на элемент в списке, а на отключенный, который был возвращен в ваше обещание. Либо вам нужно будет изменить элемент, например:

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

Или, если он становится слишком волосатым, вы можете рассмотреть массив элементов, который выглядит более как это:

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

В этот момент ваша функция обновления будет выглядеть так:

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

Ответ 4

Я только что потратил часы на эту проблему. Я не мог использовать решение $index от @eladcon, так как мой ng-repeat также использовал фильтр, чтобы индекс был неправильным, если строки/элементы были отфильтрованы.

Я думал, что смогу просто сделать это:

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

но это не работает.

Я закончил повторять массив до тех пор, пока не нашел совпадение, а затем использовал $index из итерации (а не из ng-repeat), чтобы установить элемент массива в новый элемент.

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

Смотрите здесь: https://codepen.io/anon/pen/NpVwoq?editors=0011