it-swarm.com.ru

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

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

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

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

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

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

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

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

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

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

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

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

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

<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 есть золотое правило: значения модели должны всегда иметь . в них. То есть они никогда не должны быть примитивами. См. этот SO ответ для получения дополнительной информации.


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

enter image description here

После нажатия на первый элемент области теперь выглядят так:

enter image description here

Обратите внимание, что новое свойство selected было создано в области действия ngRepeat. Область действия контроллера 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 всегда должны иметь ..

203
Josh David Miller

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

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