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

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

当单击链接时,第一个控制器在模型数组(pluginsDisplay)中添加一个小部件。小部件被推送到数组中,这个更改被反映到视图中(该视图使用 n- 重复显示数组内容) :

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

这个小部件是基于三个指令构建的,即 k2plugin、删除和调整大小。Move 指令将 span 添加到 k2plugin 指令的模板中。当单击 span 时,使用 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>

这是我的主要观点:

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);
})
};
});
159475 次浏览

Whenever you do some form of operation outside of AngularJS, such as doing an Ajax call with jQuery, or binding an event to an element like you have here you need to let AngularJS know to update itself. Here is the code change you need to do:

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

Here is the documentation on it: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply

If you add a $scope.$apply(); right after $scope.pluginsDisplayed.splice(index,1); then it works.

I am not sure why this is happening, but basically when AngularJS doesn't know that the $scope has changed, it requires to call $apply manually. I am also new to AngularJS so cannot explain this better. I need too look more into it.

I found this awesome article that explains it quite properly. Note: I think it might be better to use ng-click (docs) rather than binding to "mousedown". I wrote a simple app here (http://avinash.me/losh, source http://github.com/hardfire/losh) based on AngularJS. It is not very clean, but it might be of help.

I had the same issue. The problem was because 'ng-controller' was defined twice (in routing and also in the HTML).

There's an easy way to do that. Very easy. Since I noticed that

$scope.yourModel = [];

removes all $scope.yourModel array list you can do like this

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

The $scope.yourModel will be updated with the clonedObjects.

Hope that helps.

Remove "track by index" from the ng-repeat and it would refresh the DOM