Директива изолирует область с областью ng-repeat в AngularJS

У меня есть директива с областью выделения (так что я могу повторно использовать директиву в других местах), и когда я использую эту директиву с ng-repeat, она не работает.

Я прочитал всю документацию и ответы Qaru на эту тему и понял проблемы. Я считаю, что избегаю всех обычных ошибок.

Итак, я понимаю, что мой код выходит из строя из-за области, созданной директивой ng-repeat. Моя собственная директива создает область изоляции и выполняет двустороннюю привязку данных к объекту в родительской области. Моя директива назначит новое значение объекта этой связанной переменной, и это отлично работает, когда моя директива используется без ng-repeat (родительская переменная обновляется правильно). Однако при ng-repeat присваивание создает новую переменную в области ng-repeat, а родительская переменная не видит изменения. Все это, как ожидается, основывается на том, что я прочитал.

Я также прочитал, что при наличии нескольких директив для данного элемента создается только одна область. И что a priority может быть задано в каждой директиве для определения порядка, в котором применяются директивы; директивы сортируются по приоритету, а затем вызывается их функции компиляции (поиск приоритета слова http://docs.angularjs.org/guide/directive).

Итак, я надеялся, что смогу использовать приоритет, чтобы убедиться, что моя директива запускается первой и заканчивается созданием области изоляции, а когда ng-repeat выполняется, она повторно использует область изоляции, а не создает область, которая прототипически наследуется от родительской области. В документации ng-repeat указано, что эта директива работает на уровне приоритета 1000. Неясно, является ли 1 более высоким уровнем приоритета или уровнем более низкого приоритета. Когда я использовал уровень приоритета 1 в моей директиве, это не помогло, поэтому я попробовал 2000. Но это еще хуже: мои двухсторонние привязки становятся undefined, и моя директива ничего не отображает.

Я создал скрипт, чтобы показать мою проблему. Я прокомментировал параметр priority в моей директиве. У меня есть список объектов имен и директива под названием name-row, которая показывает поля первого и последнего имени в объекте name. Когда щелкнуто отображаемое имя, я хочу, чтобы он установил переменную selected в основной области. Массив имен, переменная selected передается в директиву name-row, используя двустороннюю привязку данных.

Я знаю, как заставить это работать, вызывая функции в основной области. Я также знаю, что если selected находится внутри другого объекта, и я привязываюсь к внешнему объекту, все будет работать. Но на данный момент меня не интересуют эти решения.

Вместо этого у меня есть следующие вопросы:

  • Как предотвратить ng-repeat от создания области, которая прототипически наследуется от родительской области, а вместо этого использовать мою выделенную область выделения?
  • Почему уровень приоритета 2000 в моей директиве не работает?
  • Используя Batarang, можно ли узнать, какой тип области использования используется?

Ответ 1

Хорошо, благодаря большому количеству комментариев выше, я обнаружил путаницу. Во-первых, несколько пунктов разъяснения:

  • ngRepeat не влияет на выбранную область выделения.
  • параметры, переданные в ngRepeat для использования в ваших атрибутах директивы, используют прототипически унаследованную область
  • причина, по которой ваша директива не работает, не имеет ничего общего с областью выделения

Здесь приведен пример того же кода, но с удаленной директивой:

<li ng-repeat="name in names"
    ng-class="{ active: $index == selected }"
    ng-click="selected = $index">
    {{$index}}: {{name.first}} {{name.last}}
</li>

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

Почему это не работает? Поскольку области в AngularJS используют прототипическое наследование. Значение selected в вашей родительской области - это примитив. В JavaScript это означает, что он будет перезаписан, когда ребенок устанавливает одно и то же значение. В области AngularJS есть золотое правило: в них всегда должны быть .. То есть, они никогда не должны быть примитивными. Подробнее см. этот ответ..


Ниже приведено изображение того, как выглядят области видимости.

enter image description here

После щелчка по первому элементу области действия теперь выглядят следующим образом:

enter image description here

Обратите внимание, что в области ngRepeat было создано новое свойство selected. Объем контроллера 003 не был изменен.

Вероятно, вы можете догадаться, что происходит, когда мы нажимаем на второй элемент:

enter image description here


Таким образом, ваша проблема на самом деле не вызвана ngRepeat - это вызвано нарушением золотого правила в AngularJS. Способ исправить это просто использовать свойство объекта:

$scope.state = { selected: undefined };
<li ng-repeat="name in names"
    ng-class="{ active: $index == state.selected }"
    ng-click="state.selected = $index">
    {{$index}}: {{name.first}} {{name.last}}
</li>

Вот второй JSFiddle, показывая, что это тоже работает.

Ниже показаны области видимости:

enter image description here

После нажатия первого элемента:

enter image description here

Здесь, по желанию, влияет область управления.

Кроме того, чтобы доказать, что это все равно будет работать с вашей директивой с областью выделения (потому что, опять же, это не имеет ничего общего с вашей проблемой), вот JSFiddle для этого тоже представление должно отражать объект. Вы заметите, что единственным необходимым изменением было использование объекта вместо примитива.

Сфера применения:

enter image description here

Скоменты после нажатия на первый элемент:

enter image description here

В заключение: еще раз, ваша проблема связана не с областью изоляции, а с тем, как работает ngRepeat. Ваша проблема в том, что вы нарушаете правило, которое, как известно, приводит к этой самой проблеме. Модели в AngularJS всегда должны иметь ..

Ответ 2

Не пытаясь не отвечать на ваши вопросы, вместо этого взгляните на следующую скрипту:

http://jsfiddle.net/dVPLM/

Ключевым моментом является то, что вместо того, чтобы пытаться бороться и изменять обычное поведение Angular, вы можете структурировать свою директиву для работы с ng-repeat, а не пытаться ее переопределить.

В вашем шаблоне:

    <name-row 
        in-names-list="names"
        io-selected="selected">
    </name-row>

В вашей директиве:

    template:
'        <ul>' +      
'            <li ng-repeat="name in inNamesList" ng-class="activeClass($index)" >' +
'                <a ng-click="setSelected($index)">' +
'                    {{$index}} - {{name.first}} {{name.last}}' +
'                </a>' +
'            </li>' +
'        </ul>'

В ответ на ваши вопросы:

  • ng-repeat создаст область действия, вы действительно не должны пытаться ее изменить.
  • Приоритет в директивах - это не просто порядок выполнения - см.: AngularJS: Как компилятор HTML упорядочивает порядок компиляции?
  • В Batarang, если вы проверяете вкладку производительности, вы можете увидеть выражения, привязанные для каждой области, и проверить, соответствует ли это вашим ожиданиям.