Использование $watch для обновления данных, ng-repeat не отражает изменения

У меня есть список продуктов питания в $scope.raw, и я хочу показать эти данные в столбцах, поэтому я немного изменяю структуру. Я делаю это в функции sortStuff() и сохраняю обновленные данные в $scope.allfood. Есть $watch, который вызывает sortStuff() каждый раз, когда что-то меняется в $scope.raw (я использую перетаскивание, чтобы изменить категорию еды):

$scope.$watch('raw', function(){
    $scope.allfood = $scope.sortStuff();
    console.log($scope.allfood);
}, true);

Вот что происходит, когда еду таскают с собой:

receive:function(event, ui) {
    var issueScope = angular.element(ui.item).scope();
    scope.$apply(function() {
        var recp = _.find(scope.raw, function(lineitem){
            return lineitem.name === issueScope.receipe.name;
        })
        recp.cat = scope.col.name;
    })

    $(ui.item).remove(); // remove DOM
}

По сути, я ищу нужный объект внутри $scope.raw и меняю cat на новую категорию еды. Я также удаляю элемент dom, потому что рассчитываю, что ng-repeat обновит представление. Кажется, это работает нормально: console.log внутри $watch показывает, что объект перемещается в нужную категорию, и данные выглядят так, как должны выглядеть. Однако визуально ng-repeat не отражает данные.

Вот jsfiddle.

Перетаскивание элемента из B в C работает нормально. Перетаскивание одного элемента из A в B приводит к исчезновению двух элементов из B... результаты очень противоречивы, и я понятия не имею, что происходит.

Любые идеи, что происходит не так? Или, может быть, какие-либо предложения по лучшему способу сделать это?


person Community    schedule 08.12.2013    source источник
comment
Вы должны использовать $watchCollection('raw', function() {}); но я сделал это и все еще вижу те же проблемы в своем приложении...   -  person philwills    schedule 08.12.2013


Ответы (1)


Проблема с вашим кодом заключается в том, что директива ng-repeat добавляет свойство $$hashKey к каждому элементу в списке. Это свойство используется директивой для связывания элементов DOM с элементами массива.

Поскольку вы передаете элементы по ссылке, директива ng-repeat записывает свойство $$hashKey непосредственно в объекты вашего массива $scope.raw. Простой обходной путь — скопировать объекты перед их вставкой в ​​объект $scope.allfood.

_.each($scope.raw, function(recp){
    recp = _.clone(recp);
    switch(recp.cat){
        ...
    }
});

Теперь ng-repeat обновляет объекты $scope.allfood, а объекты $scope.raw остаются нетронутыми.

Смотрите обновленную скрипку:

http://jsfiddle.net/b8Fa7/5/

person Lorenz    schedule 08.12.2013
comment
Спасибо, это работает! Однако два вопроса: возможно ли клонировать объект без таких свойств, как $$hashKey, которые были добавлены angular? Кроме того, вы думаете, что есть лучший способ сделать это? Тот факт, что angular регенерирует весь $scope.allfood при каждом изменении, не может быть хорошим для производительности, и я хотел бы как-то избежать этого. - person ; 08.12.2013
comment
angular.copy позволяет создать глубокую копию массива или объекта без таких свойств, как $$hashKey: docs. angularjs.org/api/angular.copy - person Lorenz; 09.12.2013
comment
Вероятно, вы могли бы отслеживать все изменения в массиве $scope.raw и изменять объект $scope.allfood только на основе отслеживаемых изменений. Я не думаю, что это приносит заметное улучшение производительности, пока список невелик. - person Lorenz; 09.12.2013
comment
angular.copy делает то, что мне нужно. Спасибо! - person ; 09.12.2013