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