我已经做了 working Plunker 来测试。
我有一个ng-repeat,它只列出了小猫的名字和id。我还有一个输入表单来接收新猫咪的名字。然后,我有3个按钮,每个按钮访问一个不同的$ scope函数,它将以不同的方式操作$ scope。
每个功能的目标是从输入表单中读取kitty的名称,读取猫咪上使用的最后一个ID 数组,将id + 1 分配给新的kitty,将新的kitty 推送到kitty数组并删除表单数据。
第一个功能,$scope.addFullScope()
,
将不会收到任何参数,并会阅读和操纵$scope
。
第二个功能,$scope.addJustDelete(kitty, kitties)
,
将同时接收新的kitty
和kitties
数组作为参数。但是,最后,要清理表单,它将执行$scope.kitty = undefined
第三个功能,$scope.addNoScope(kitty, kitties)
,
将同时接收新的kitty
和kitties
数组作为参数。但是,最后,为了清理表单,它将kitty = undefined
。 但表单不会清理!并且所有内容都会出现问题。
附录:
HTML
<body ng-app='app' ng-controller="ctrl">
<h3 ng-repeat="kitty in kitties">
{{kitty.name}}: {{kitty.id}} //Kitties list
</h3>
<input placeholder='Kitty name to add' class='form form-control'
type="text" ng-model="kitty.name" />
<h3> $scope use on adding kitty:</h3>
<button ng-click="addFullScope()">Full Scope.</button>
<button ng-click="addJustDelete(kitty, kitties)">Just delete.</button>
<button ng-click="addNoScope(kitty, kitties)">None. Buggy</button>
</body>
控制器:
.controller('ctrl', function($scope) {
$scope.kitties = [
//Let's imagine kitties in here.
{name: 'Purple kitty', id:35},
//Kittie 36 died in a car accident. :(
{name: 'Rodmentou cat', id: 37},
{name: 'Fatty kitty', id: 38}
];
$scope.addFullScope = function () {
var size = $scope.kitties.length;
var id = $scope.kitties[size-1].id + 1;
$scope.kitty.id = id;
$scope.kitties.push($scope.kitty);
$scope.kitty = undefined;
};
$scope.addJustDelete = function (kitty, kitties) {
var size = kitties.length;
var id = kitties[size-1].id + 1;
kitty.id = id;
kitties.push(kitty);
$scope.kitty= undefined;
};
$scope.addNoScope = function (kitty, kitties) {
var size = kitties.length;
var id = kitties[size-1].id + 1;
kitty.id = id;
kitties.push(kitty);
kitty = undefined; //Don't work
};
});
答案 0 :(得分:9)
引擎盖下Angular需要一种方法来观察ngModel是否被更改并使用$ scope。$ watch来完成此任务。 $ watch被视为Equality Watcher,它会检查一个特定属性是否已更改,而不是基础上的对象是否已更改,这将是一个参考观察器。
上找到了一个很好的描述和形象因此,您的代码的问题在于您正在设置kitty = undefined
,但是ngModel和扩展名$ scope。$ watch正在kitty.name
中寻找更改。为证明这种情况,我在kitty.name
添加了一个手表,当您设置kitty = undefined
时,手表永远不会被触发。将kitty设置为undefined会更改底层对象,但不会更改它的属性(非常令人困惑)!我创建了一个plunker example with updated code。
<强> HTML 强>
为了清楚起见,我创建了我们正在更新的模型kitten
,因此它不会与我们正在迭代的kitties
混淆。
<h3 ng-repeat="kitty in kitties">
{{kitty.name}}: {{kitty.id}}
</h3>
<input placeholder='Kitty name to add' class='form form-control' type="text" ng-model="kitten.name" />
<h3> $scope use on adding kitty:</h3>
<button class='btn btn-success' ng-click="addNoScope(kitten, kitties)">None. Buggy</button>
<button class='btn btn-danger' ng-click="noWatch(kitten)">Full Scope.</button>
<强>的Javascript 强>
最新的更新是从模型的小猫中创建克隆小猫,因此它不会在数组中引用。此外,当我们要重置模型的小猫时,我们会直接设置kitten.name
和kitten.id
属性。
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.kitten = {
name: undefined, id: undefined
};
$scope.$watch('kitten.name', function() { console.log("changed"); }, true);
$scope.kitties = [
//Let's imagine kitties in here.
{name: 'Purple kitty', id:35},
//Kittie 36 died in a car accident. :(
{name: 'Rodmentou cat', id: 37},
{name: 'Fatty kitty', id: 38}
];
$scope.addNoScope = function (kitten, kitties) {
if (!$scope.kitten || !$scope.kitten.name) return;
var size = kitties.length;
var id = kitties[size-1].id + 1;
var newKitten = {
name: kitten.name, id: id
}
kitties.push(newKitten);
kitten.name = undefined;
kitten.id = undefined;
// kitten = undefined;
};
$scope.noWatch = function (kitten) {
kitten = undefined;
console.log('not watching');
};
});
答案 1 :(得分:3)
这是因为函数$scope.addNoScope(kitty, kitties)
仅接收对象kitty
的“引用副本”,因此更改引用仅影响此副本而不影响原始引用。 Check this thread.
因此,在您的情况下,$scope.kitty
仍然指向具有姓氏和ID的同一对象。当您按addNoScope
时,另一次有角度尝试再次推送同一个对象,因为角度不允许ng-repeat
中的重复项,因此会track by
,除非您添加kitty
。< / p>
如果您需要在不使用范围的情况下执行此操作,解决方案是将包含var obj = {id:1};
function changeReference (obj){
//changing the reference to an entirely new object
obj = {id:2}
}
function changeProperty (obj){
//changing a property of the current object
obj.id = 2
}
changeReference(obj);
console.log(obj.id);
//1 as original obj is not affected
changeProperty(obj);
console.log(obj.id);
//2
的对象作为属性传递,以便在更改传递的对象的属性时,它会反映在原始对象上对象,如本例所示
$scope.addNoScope = function (fields, kitties) {
var size = kitties.length;
var id = kitties[size-1].id + 1;
fields.kitty.id = id;
kitties.push(fields.kitty);
fields.kitty = undefined; //Don't work
};
所以你的代码变成了
<input placeholder='Kitty name to add' class='form form-control' type="text" ng-model="fields.kitty.name" />
<h3> $scope use on adding kitty:</h3>
<button class='btn btn-danger' ng-click="addFullScope()">Full Scope.</button>
<button class='btn btn-warning' ng-click="addJustDelete(fields.kitty, kitties)">Just delete.</button>
<button class='btn btn-success' ng-click="addNoScope(fields, kitties)">None. Buggy</button>
和html
$scope.kitty
并且不要忘记在其他功能中将$scope.fields.kitty
更改为std::cin
,否则它们会中断。
答案 2 :(得分:2)
要使用独立的功能操作小猫,并使用单独的功能重置表单,请尝试:
$scope.addNoScope = function (kitty, kitties) { manipulateKitties(kitty, kitties); resetForm(); }; function manipulateKitties(kitty, kitties) { var size = kitties.length; var id = kitties[size-1].id + 1; kitty.id = id; kitties.push(kitty); } function resetForm() { $scope.kitty= undefined; }
答案 3 :(得分:1)
这是不可能的,因为JS遵循Java的pass-by-ref-ish参数传递方法。这里有一些与你的案例相关的例子
function double(x) {
x.val = 2 * x.val;
}
function doubleToo(x) {
x = 2 * x;
}
var data = {
val: 2
};
document.write("Initial: " + data.val + "<br />");
double(data); //data.val now = 4
document.write("Double: " + data.val + "<br />");
doubleToo(data.val); //data.val is still 4
document.write("DoubleToo:" + data.val);
&#13;
对参数的赋值操作对传入的实际变量没有影响。这就是为什么第一个更新对象内容的例子有效,第二个例子(尝试重新分配)价值全部在一起)没有。
因此,在您的情况下,如果该函数绝对必须更新$scope
中的变量,则必须接收$scope
作为参数(或包含对{{1}的引用的某个对象})。就像在该示例中一样,它必须接收$scope
来更新data
。
答案 4 :(得分:0)
您只需按以下方式重构您的功能:
$scope.addNoScope = function (kitty, kitties) {
var size = kitties.length;
kitties.push({ //you need to create a new object
id : kitties[size-1].id + 1,
name : kitty.name
});
kitty.name = undefined;
};
重要的是要记住JavaScript是按值传递变量,这意味着参数kitty实际上是范围$ scope.kitty中的变量。所以你把实际的范围变量推送到你的数组中,然后使它等于undefined,这就是bug结果的原因。