【问题标题】:ng-change event only fires once for each radio button when created with ng-repeat使用 ng-repeat 创建时,每个单选按钮的 ng-change 事件仅触发一次
【发布时间】:2014-05-21 17:37:17
【问题描述】:

我创建了一个带有单选按钮的 AngularJS 指令来控制我的页面将查询哪个环境并将其添加到我的页面中。我正在使用双向绑定将名为environment 的局部范围变量映射到同名的应用程序变量。当我在模板中显式创建单选按钮时,它似乎运行良好(在我的实际代码中,我使用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>

Here is the working version fiddle 和我的指令代码的相关部分:

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

}

还有here is the version that only works once:

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 的新手,所以我完全有可能犯了一个非常基本的错误。谁能指出我正确的方向?

更新 根据 callmekatootie 的建议,我将相关事件从 ng-change 更改为 ng-click,并且每次都会触发。现在可以解决此问题,但我最初使用ng-change,因为我认为ng-click 不会应用于由单击文本标签而不是输入本身引起的更改,但实际上确实如此。不过,仍然不明白为什么 ng-change 只触发一次。

【问题讨论】:

  • 尝试使用ng-click 而不是ng-change - 虽然是另一种选择。我不知道为什么ng-change 每次更改都不会触发。
  • @callmekatootie - 这似乎解决了它。我最初将它命名为ng-change,因为我认为ng-click 不会应用于标签,所以我想捕获事件然而它发生了。奇怪的是,点击文本似乎也会在输入本身上触发ngClick——我没想到会这样。这将帮助我继续前进,尽管我仍然不确定为什么它只适用于ng-change
  • 这是由于原型继承在 JavaScript 中的工作方式以及 ng-repeat 中的每个元素创建一个新范围的事实。如果您仍然需要它,我可以在答案中解释。
  • @tasseKATT - 如果您不介意,那就太好了。似乎它在第一次被点击时设置了它的状态,而当另一个被选中时它没有被更新。这似乎与以下事实有关:如果我从单选按钮中删除 name 属性,它们就会停止协同工作。我不认为它们像我想象的那样与environment 变量相关联。

标签: angularjs angularjs-directive


【解决方案1】:

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

Angular wiki 对作用域继承的工作原理(以及常见的陷阱)有相当好的(和彻底的)解释。

This answer 到一个非常相似(本质上)的问题几乎可以解释这个问题。

在您的具体情况下,您可以引入一个对象(例如local)并将environment 分配为其属性):

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

然后你应该将你的模板绑定到那个“封装”的属性:

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

另请参阅此short demo


更新

此答案旨在指出问题的根源。
绝对推荐使用不同的实现(如tasseKATTLeon 提出的实现)(并且更“Angularish”)。

【讨论】:

  • 由于我正在学习,我很高兴了解它的为什么 - 感谢您的帮助。我必须承认,我不确定哪个相当不同的答案是“最好的”。 DML 绝对是最简单(且有效)的。
  • 我自己也不能决定 :) 我通常不...我实际上从不使用$parent(出于几个不同的原因),但我真的想不出有什么问题按照 Leon 的建议使用它。另一方面,如果您可以忍受将environment 转换为控制器范围内的对象,那么 tasseKATT 的解决方案更直接。
【解决方案2】:

使用对象而不是原始值:

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

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

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

关于原型继承的非常好的帖子可以在here找到。

【讨论】:

  • 我使用$watch 是因为我启动了一项服务,以便在用户更改环境时从服务器中提取数据。我在各个地方写出了environment 的值,只是为了看看它们如何/何时改变。这听起来像是对$watch 的合理使用吗?
  • 是的,ng-change 和 $watch 都可以用于该场景。
  • 这和$parent 的答案似乎都有效,但我以这种方式实现了它,因为我认为它与我最初想象的双向绑定工作方式更接近。 $parent 感觉有点像“是的,这是你的局部变量,但是......”
  • 我尝试了您的解决方案,但它对我不起作用,我在 ng-repeat 中有 ng-repeat 。
【解决方案3】:

您在 ng-change 事件中使用 environment 而不是 $parent.enviorement,该事件与重复范围相关联,而不是环境变量所在的 ng-repeat 父范围,

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

ng-model="$parent.enviroment"

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

【讨论】:

  • 这让ng-change 工作了——谢谢。所以在这种情况下$parent 指的是指令的environment,而不是应用程序中的同名变量,对吧?由于双向绑定,它只是冒泡到顶部,对吧?
  • this 指的是位于指令范围内的环境变量,它恰好是控制器中的同一个变量,因为您使用 'environment: '='' 来调整它删除我在小提琴中演示的 ng-change
猜你喜欢
  • 2014-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-11
  • 1970-01-01
相关资源
最近更新 更多