问题 ng-change事件仅在使用ng-repeat创建时为每个单选按钮触发一次


我创建了一个带有单选按钮的AngularJS指令来控制我的页面将查询的环境并将其添加到我的页面。我使用双向绑定来映射一个名为的局部范围变量 environment 到具有相同名称的app变量。当我在模板中显式创建单选按钮时,它似乎运行良好(在我使用的实际代码中) templateUrl 相反,但仍然有同样的问题)。

<div>
    <label><input type="radio" name="env" ng-model="environment" ng-change="changeEnvironment(1)" value="1" />Testing</label>
    <label><input type="radio" name="env" ng-model="environment" ng-change="changeEnvironment(2)" value="2" />Production</label>
</div>

我可以根据需要多次选择每个选项,并且值一直到应用程序的范围变量。

我想改变要使用的单选按钮的创建 ng-repeat 在一组选择对象上。它创建了选项,并捕获了 ngChange 事件,但每个选择只有一次。

<label ng-repeat="choice in choices">
    <input type="radio" name="env" ng-model="environment" ng-change="changeEnvironment(choice)" value="{{ choice.id }}" />{{ choice.name }}
</label>

这是工作版小提琴 以及我的指令代码的相关部分:

template: '<div>'
            + '<label><input type="radio" name="env" ng-model="environment" ng-change="changeEnvironment(1)" value="1" />Testing</label>'
            + '<label><input type="radio" name="env" ng-model="environment" ng-change="changeEnvironment(2)" value="2" />Production</label>'
            + '<div>Directive environment: {{ environment }}</div>'
            + '</div>',
link: function(scope, element, attributes) {

    scope.changeEnvironment = function(choiceID) {
        console.log('SELECTED environment ' + choiceID);
        scope.environment = choiceID;
        console.log('directive environment = ' + scope.environment);
    };

}

这是仅适用一次的版本

template: '<div><label ng-repeat="choice in choices">'
            + '<input type="radio" name="env" ng-model="environment" ng-change="changeEnvironment(choice)" value="{{ choice.id }}" />{{ choice.name }}'
            + '</label>'
            + '<div>Directive environment: {{ environment }}</div>'
            + '</div>',
link: function(scope, element, attributes) {
    scope.choices = [
        { id: 1, name: "Testing" },
        { id: 2, name: "Production" }
    ];

    scope.changeEnvironment = function(choice) {
        console.log('SELECTED environment ' + choice.id);
        scope.environment = choice.id;
        console.log('directive environment = ' + scope.environment);
    };

}

我对AngularJS来说是全新的,所以我完全有可能犯了一个非常基本的错误。谁能指出我正确的方向?

UPDATE 根据callmekatootie的建议,我改变了有问题的事件 ng-change 至 ng-click,它每次都会发射。这将作为现在的解决办法,但我最初使用 ng-change 因为我没想到 ng-click 将适用于通过单击文本标签而不是输入本身而导致的更改,但事实上确实如此。仍然不明白为什么 ng-change 但是只会发射一次。


5815
2018-05-21 17:37


起源

尝试使用 ng-click 代替 ng-change  - 一个替代方案。我不知道为什么 ng-change 每次更改都不会启动。 - callmekatootie
@callmekatootie - 这似乎解决了它。我原来是这样的 ng-change 因为我没想到 ng-click 将适用于标签,所以我想捕获该事件 然而 发生了。奇怪的是点击文本似乎也开始了 ngClick 关于输入本身 - 我没想到。这将帮助我前进,虽然我仍然不确定 为什么 它只适用于一次 ng-change。 - Danny
这是因为原型继承在JavaScript中的工作原理以及ng-repeat中的每个元素都创建了一个新范围。如果你仍然需要,我可以在答案中解释。 - tasseKATT
@tasseKATT - 如果你不介意,这将是伟大的。看起来它似乎是在第一次点击时设置其状态,并且当另一个被选中时它没有被更新。这似乎与以下事实有关:如果我删除了 name 来自单选按钮的属性,它们停止协同工作。我不认为他们与...有关 environment 我认为他们是变数。 - Danny


答案:


使用对象而不是原始值:

app.controller('mainController', ['$scope', function (scope) {
  scope.environment = { value: '' };
}]);

去掉 ng-change 和 $watchng-model 就足够了(除非我误解了用例)。

演示:  http://jsfiddle.net/GLAQ8/

可以找到关于原型继承的非常好的帖子 这里


4
2018-05-21 19:52



我在用 $watch 因为我会在用户更改环境时启动服务以从服务器提取数据。我正在写出的价值观 environment 在各个地方只是为了看他们如何/何时改变。这听起来像是合理使用 $watch? - Danny
是的,ng-change和$ watch都可用于该场景。 - tasseKATT
这似乎是这个和答案 $parent 工作,但我已经这样实现了,因为我认为它与我最初描绘双向绑定工作的方式更紧密地对齐。 $parent 感觉有点像“是的,这是你的本地变量,但......” - Danny
我尝试使用你的解决方案,但它对我不起作用,我在ng-repeat内部进行了重复。 - Anuj


问题的原因是 ngRepeat 将为其子项创建新的范围(以及原型继承如何影响范围)。

Ang wiki 对范围继承如何工作(以及常见的陷阱)有一个相当好(和彻底)的解释。

这个答案 一个非常相似(在本质上)的问题几乎解释了这个问题。

在您的具体情况下,您可以介绍一个对象(例如 local)并分配 environment 作为其财产):

scope.local = {environment: scope.environment};
scope.changeEnvironment = function(choice) {
    scope.environment = choice.id;
};

然后,您应该将模板绑定到“encaplulated”属性:

<input ... ng-model="local.environement" ... />

另见,这一点 简短的演示


UPDATE

这个答案旨在指出问题的根源。
一个不同的实现(如提议的那些) tasseKATT 要么 莱昂)绝对是推荐的(而且更多的是“Angularish”)。


7
2018-05-21 19:48



+1解释:) - tasseKATT
自从我学习以来,我很高兴能够学习 为什么 它 - 谢谢你的帮助。我必须承认,我不确定哪个相当不同的答案是“最好的”。 DML绝对是最简单的(也是最好的)。 - Danny
我不能自己决定:)我通常不会......我实际上从不使用 $parent (由于几个不同的原因),但我真的想不出使用它有什么不对,莱昂建议。另一方面,如果你可以忍受转换 environment 对于控制器范围内的对象,那么tasseKATT的解决方案更直接。 - gkalpak


你在ng-change事件中使用enviorement而不是$ parent.enviorement,它与重复范围有关,而不是enviorement变量所在的ng-repeat父范围,

http://jsfiddle.net/cJ4Wb/7/

ng-model="$parent.enviroment"

请注意,在这种情况下,您甚至不需要事件来保持更新环境,并且模型中的任何更改都将在单选按钮中反映


2
2018-05-21 19:46



这得到了 ng-change 工作 - 谢谢。所以在这种情况下 $parent 指的是指令 environment,而不是应用程序中同名的变量,对吧?由于双向绑定,它只是冒泡到顶端,对吧? - Danny
这引用了环境变量,它存在于你的指令范围内,这个变量在控制器中发生的变量是相同的,因为你使用'environment:'=''来判断它并且你可以删除ng-change,因为我说明了在小提琴 - Dayan Moreno Leon