Событие ng-change срабатывает только один раз для каждого переключателя при создании с помощью ng-repeat

Я создал директиву 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>

Вот скрипта рабочей версии и соответствующие части моего директивного кода:

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, поэтому вполне возможно, что я делаю очень основную ошибку. Может ли кто-нибудь указать мне в правильном направлении?

ОБНОВЛЕНИЕ. В соответствии с предложением callmekatootie я изменил рассматриваемое событие от ng-change до ng-click, и оно срабатывает каждый раз. На данный момент это будет работать, но я изначально использовал ng-change, потому что я не думал, что ng-click будет применяться к изменениям, вызванным щелчком по текстовой метке, а не самим входом, но на самом деле это так. По-прежнему не получается, почему ng-change срабатывает только один раз.

Ответ 1

Используйте объект вместо примитивного значения:

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

Удалите ng-change и $watch. ng-model будет достаточно (если я не ошибаюсь в случае использования).

Демо: http://jsfiddle.net/GLAQ8/

Очень хорошее сообщение о прототипном наследовании можно найти здесь.

Ответ 2

Причиной проблемы является то, что ngRepeat создаст новые области для своих дочерних элементов (наряду с тем, как прототипное наследование влияет на области видимости).

Angular wiki имеет довольно хорошее (и полное) объяснение того, как работает наследование областей ( и общие подводные камни).

Этот ответ к очень похожей проблеме (в природе) в значительной степени объясняет эту проблему.

В вашем конкретном случае вы можете ввести объект (например, local) и назначить environment как его свойство):

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

Затем вы должны привязать свой шаблон к этому свойству "encaplulated":

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

См. также эту короткую демонстрацию.


UPDATE

Этот ответ намеревается указать корень проблемы.
Разная реализация (например, предложенная tasseKATT или Leon), безусловно, рекомендуется (и еще более "Угловая" ").

Ответ 3

вы используете enviorement вместо $parent.enviorement в своем событии ng-change, которое привязано к области повтора, а не к родительской области ng-repeat, в которой живет переменная envorment,

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

ng-model="$parent.enviroment"

обратите внимание, что в этом случае вам даже не нужны события, чтобы обновлять настройки и любые изменения в модели будут обновляться в переключателях