AngularJS:从模型数组拼接模型元素时,不会更新 ng-repeat 列表

Posted

技术标签:

【中文标题】AngularJS:从模型数组拼接模型元素时,不会更新 ng-repeat 列表【英文标题】:AngularJS: ng-repeat list is not updated when a model element is spliced from the model array 【发布时间】:2013-03-06 17:12:53 【问题描述】:

我有两个控制器,并通过 app.factory 函数在它们之间共享数据。

单击链接时,第一个控制器会在模型数组 (pluginsDisplayed) 中添加一个小部件。小部件被推送到数组中,并且此更改反映到视图中(使用 ng-repeat 显示数组内容):

<div ng-repeat="pluginD in pluginsDisplayed">
    <div k2plugin pluginname="pluginD.name" pluginid="pluginD.id"></div>
</div>

该小部件建立在三个指令之上:k2plugin、remove 和 resize。 remove 指令将跨度添加到 k2plugin 指令的模板。当单击所述跨度时,共享数组中的右侧元素将被删除为Array.splice()。共享数组已正确更新,但更改反映在视图中。但是,当添加另一个元素时,删除后,视图会正确刷新,并且不会显示先前删除的元素。

我做错了什么?你能解释一下为什么这不起作用吗? 有没有更好的方法来做我想要用 AngularJS 做的事情?

这是我的 index.html

<!doctype html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js">
        </script>
        <script src="main.js"></script>
    </head>
    <body>
        <div ng-app="livePlugins">
            <div ng-controller="pluginlistctrl">
                <span>Add one of pluginList.length plugins</span>
                <li ng-repeat="plugin in pluginList">
                    <span><a href="" ng-click="add()">plugin.name</a></span>
                </li>
            </div>
            <div ng-controller="k2ctrl">
                <div ng-repeat="pluginD in pluginsDisplayed">
                    <div k2plugin pluginname="pluginD.name" pluginid="pluginD.id"></div>
                </div>
            </div>
        </div>
    </body>
</html>

这是我的 main.js:

var app = angular.module ("livePlugins",[]);

app.factory('Data', function () 
    return pluginsDisplayed: [];
);

app.controller ("pluginlistctrl", function ($scope, Data) 
    $scope.pluginList = [name: "plugin1", name:"plugin2", name:"plugin3"];
    $scope.add = function () 
        console.log ("Called add on", this.plugin.name, this.pluginList);
        var newPlugin = ;
        newPlugin.id = this.plugin.name + '_'  + (new Date()).getTime();
        newPlugin.name = this.plugin.name;
        Data.pluginsDisplayed.push (newPlugin);
    
)

app.controller ("k2ctrl", function ($scope, Data) 
    $scope.pluginsDisplayed = Data.pluginsDisplayed;

    $scope.remove = function (element) 
        console.log ("Called remove on ", this.pluginid, element);

        var len = $scope.pluginsDisplayed.length;
        var index = -1;

        // Find the element in the array
        for (var i = 0; i < len; i += 1) 
            if ($scope.pluginsDisplayed[i].id === this.pluginid) 
                index = i;
                break;
            
        

        // Remove the element
        if (index !== -1) 
            console.log ("removing the element from the array, index: ", index);
            $scope.pluginsDisplayed.splice(index,1);
        

    
    $scope.resize = function () 
        console.log ("Called resize on ", this.pluginid);
    
)

app.directive("k2plugin", function () 
    return 
        restrict: "A",
        scope: true,
        link: function (scope, elements, attrs) 
            console.log ("creating plugin");

            // This won't work immediately. Attribute pluginname will be undefined
            // as soon as this is called.
            scope.pluginname = "Loading...";
            scope.pluginid = attrs.pluginid;

            // Observe changes to interpolated attribute
            attrs.$observe('pluginname', function(value) 
                console.log('pluginname has changed value to ' + value);
                scope.pluginname = attrs.pluginname;
            );

            // Observe changes to interpolated attribute
            attrs.$observe('pluginid', function(value) 
                console.log('pluginid has changed value to ' + value);
                scope.pluginid = attrs.pluginid;
            );
        ,
        template: "<div>pluginname <span resize>_</span> <span remove>X</span>" +
                       "<div>Plugin DIV</div>" +
                  "</div>",
        replace: true
    ;
);

app.directive("remove", function () 
    return function (scope, element, attrs) 
        element.bind ("mousedown", function () 
            scope.remove(element);
        )
    ;

);

app.directive("resize", function () 
    return function (scope, element, attrs) 
        element.bind ("mousedown", function () 
            scope.resize(element);
        )
    ;
);

【问题讨论】:

【参考方案1】:

每当您在 AngularJS 之外进行某种形式的操作时,例如使用 jQuery 进行 Ajax 调用,或者将事件绑定到您在此处的元素,您需要让 AngularJS 知道要更新自己。这是您需要做的代码更改:

app.directive("remove", function () 
    return function (scope, element, attrs) 
        element.bind ("mousedown", function () 
            scope.remove(element);
            scope.$apply();
        )
    ;

);

app.directive("resize", function () 
    return function (scope, element, attrs) 
        element.bind ("mousedown", function () 
            scope.resize(element);
            scope.$apply();
        )
    ;
);

这是关于它的文档:https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply

【讨论】:

考虑在传递给 $apply 的表达式/函数中移动 scope.remove(element) 和 scope.resize(element)。 @PerHornshøj-Schierbeck 我同意,否则如果发生错误,Angular 将不会意识到错误。 小心!通常,Angular 在必要时会调用摘要循环,而 $apply 将手动调用它。手动调用它通常是一种不好的做法,因为我们可能会犯优化错误,并且可能会消耗资源。 我不明白删除或调整大小的内容是什么。【参考方案2】:

如果您在$scope.pluginsDisplayed.splice(index,1); 之后添加$scope.$apply();,那么它可以工作。

我不确定为什么会这样,但基本上当 AngularJS 不知道 $scope 已更改时,它需要手动调用 $apply。我也是 AngularJS 的新手,所以无法更好地解释这一点。我也需要深入研究一下。

我发现this awesome article 解释得非常恰当。 注意:我认为使用 ng-click (docs) 而不是绑定到“mousedown”可能会更好。我在这里基于 AngularJS 编写了一个简单的应用程序(http://avinash.me/losh,源代码http://github.com/hardfire/losh)。它不是很干净,但它可能会有所帮助。

【讨论】:

【参考方案3】:

我有同样的问题。问题是因为“ng-controller”被定义了两次(在路由和 HTML 中)。

【讨论】:

【参考方案4】:

从 ng-repeat 中删除“按索引跟踪”,它会刷新 DOM

【讨论】:

【参考方案5】:

有一个简单的方法可以做到这一点。很容易。因为我注意到了

$scope.yourModel = [];

删除您可以这样做的所有 $scope.yourModel 数组列表

function deleteAnObjectByKey(objects, key) 
    var clonedObjects = Object.assign(, objects);

     for (var x in clonedObjects)
        if (clonedObjects.hasOwnProperty(x))
             if (clonedObjects[x].id == key)
                 delete clonedObjects[x];

    $scope.yourModel = clonedObjects;

$scope.yourModel 将使用 clonedObjects 进行更新。

希望对您有所帮助。

【讨论】:

以上是关于AngularJS:从模型数组拼接模型元素时,不会更新 ng-repeat 列表的主要内容,如果未能解决你的问题,请参考以下文章

在angularjs中选中checkall和checkbox时,推送并拼接成数组

无法从范围数组AngularJS中删除元素

从服务器推送websocket数据后Angularjs模型发生变化

Angularjs模型数组不使用模板更新

如何在AngularJS的二维数组中找到访问元素

在angularjs中删除动画中间的元素