在AngularJs中,为什么一个范围变量在更改时更新,另一个非常相似的变量不更新?

时间:2014-07-14 16:29:28

标签: angularjs

我有一个ItemsService,它为一对控制器/视图提供了一系列项目。

在第一种情况(列表视图)中,服务中某个项目的更改会导致UI发生变化 - 一切都很好。

在第二种情况(详细视图)中,项目绑定正常但如果项目在服务中发生更改则不会更新。

我错过了什么?

Plunker:http://plnkr.co/edit/Jlk2xFfry4N8Wqfw31Em?p=preview

服务:

app.factory('ItemsService', function ($rootScope) {

    // omitted code to update items - this works fine

    var itemsById = [];
    var itemsArray = [];

    function updateItems(itemUpdate) {
        itemsById[itemUpdate.Id] = itemUpdate;

        while (itemsArray.length > 0) {
            itemsArray.pop();
        }

        for (var key in itemsById) {
            itemsArray.push(itemsById[key]);
        };
    }

    return {
        items: itemsArray,
        itemsById: itemsById,
    }
});

列出项目的控制器:

app.controller('ItemsController', function ($scope, ItemsService) {
    $scope.items = ItemsService.items;
});

列表视图:

<table class="table" data-ng-controller="ItemsController">
    <tr data-ng-repeat="item in items">
        <td>{{item.Name}}</td>
    </tr>
</table>

详情视图的控制器:

//$stateParams is part of Angular UI Routing - this works because the correct item is selected and displayed - it just doesn't update
app.controller('ItemController', function ($scope, $stateParams, ItemService) {
    $scope.item = ItemService.itemsById[$stateParams.id];
});

详情视图:

<div data-ng-controller="ItemController">
    {{item.Name}}
</div>

修改

如果我在一个函数中包装调用以在第二种情况下分配项目,那么它可以工作:

app.controller('ItemController', function ($scope, $stateParams, ItemService) {
    $scope.item = function () { return ItemService.itemsById[$stateParams.id]; }
});

详情视图:

<div data-ng-controller="ItemController">
    {{item().Name}}
</div>

但我的理解是,绑定到像这样的函数调用是不好的做法..

1 个答案:

答案 0 :(得分:1)

在您的示例代码中实际上有很多事情同时进行,我不确定代码的哪一部分实际上是您关心的,以及哪些部分仅用于演示。

这是一个正在运作的Plunkr: http://plnkr.co/edit/mjUtRSfFwYLGPC17O0st?p=preview

我改变了一些事情。首先,在工厂中,您返回的是硬编码的对象数组,但是初始化它们的方式使每个对象都是唯一的,而不是相同的元素。因此,更新它们只会在每个位置更新一个副本:

return {
    items: [{Id: '1', Name: 'TestItem1'}, {Id: '2', Name: 'TestItem2'}],
    itemsById: {'1': {Id: '1', Name: 'TestItem1'}, '2': {Id: '2', Name: 'TestItem2'} },
}

也许你刚刚为演示目的做了这个?但是在更新例程中,当您改变这些数组时,AngularJS无法跟踪这些更改,因为它们都会在每次调用时指向不同的对象。

接下来,我将服务从工厂更改为服务。它们都可以工作,但我发现当你在这样的事情上做出这样的事情时,服务模式通常更容易理解 - 在API面前单身。 Javascript非常容易受到如何引用对象的小错误的影响。例如,当你这样做时:

itemsById[itemUpdate.Id] = itemUpdate;
这会让你遇到很多麻烦,特别是在Angular。往往会发生在其他地方你做这样的事情:

var myItem = ThatService.itemsById[123];

但是现在你有了旧拷贝的引用。使用新副本更新条目时,屏幕不会更新。这是因为myItem指向旧副本,即使您已更新了包含项目的数组。也就是说,JS中的引用指向目标对象,而不是指向其存储集合中的插槽。更新存储集合需要更像:

myCollection[Id] = myCollection[Id] || {};
angular.extend(myCollection[Id], myNewData);

这种方式无论谁有对象的引用,都会得到所需的更新。

如果您有任何疑问,请查看Plunkr并告诉我。如果您需要有关确切用例的详细信息,我很乐意回复您的评论!