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


100

我有两个控制器,并使用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);
        })
    };
});

Answers:


131

每当您在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


4
考虑将scope.remove(element)和scope.resize(element)移到传递给$ apply的表达式/函数中。
PerHornshøj-Schierbeck2014年

1
@PerHornshøj-Schierbeck我同意,否则如果发生错误,Angular将不会意识到错误。
Jim Aho 2015年

2
小心 !通常,Angular在需要时调用摘要循环,而$ apply将手动调用它。手动调用它通常是一个不好的做法,因为我们可能会犯优化错误,并且可能会浪费资源。
Alex

我不明白删除或调整您放置的大小是什么。
Desarrollo Desafio de Guerrero

53

如果您$scope.$apply();$scope.pluginsDisplayed.splice(index,1);之后添加权利,则它会起作用。

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

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


7

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



0

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

$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更新。

希望能有所帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.