Два способа привязки не работает в директиве с раскрываемый область


у меня есть текстовое поле в контроллере, который привязан к модели name. Есть директива внутри контроллера и есть еще одно текстовое поле внутри директивы, которая привязана к той же модели name:

<div class="border" ng-controller="editCtrl">
   Controller: editCtrl <br/>
   <input type="text" ng-model="name" />
   <br/>
   <tabs>
      Directive: tabs <br/>
      <input type="text" ng-model="name"/>
   </tabs>
</div>

mod.directive('tabs', function() {
  return {
    restrict: 'E',
    transclude: true, 
    template:
      '<div class="border" ng-transclude></div>',
  };
});

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

см. пример в: http://jsfiddle.net/uzairfarooq/MNBLd/

Я также пробовал использовать двухстороннюю привязку attr (scope: {name: '='}), но это дает синтаксическую ошибку.И используя scope: {name: '@'} имеет тот же эффект.

любая помощь была бы весьма признательна.

в дополнение к принятому ответу,в этой статье действительно помог мне в понимании прототипического наследования в детских scpoes. Я очень рекомендую всем, у кого есть проблемы с областями, чтобы прочитать его тщательно.

3 58

3 ответа:

директива с transclude: true приводит к директиве, создающей новую (закодированную) дочернюю область. Эта новая область прототипически наследуется от родительской области. В вашем случае родительская область-это область, связанная с контроллером editCtrl.

использование двусторонней привязки данных в дочерней области (т. е. ng-model) для привязки к свойству родительской области, содержащему примитивное значение (например, name) всегда вызывает проблемы-ну, я должен сказать, что он не работает, как ожидалось. Когда свойство scope изменяется в дочернем элементе (например, вы вводите во второе текстовое поле) дочерний элемент создает новое свойство scope, которое скрывает/затеняет родительское свойство scope с тем же именем. Если родительское свойство содержит примитивное значение, то это значение (по существу) копируется в дочернее свойство при создании дочернего свойства. Будущие изменения в дочерней области (например, второе текстовое поле) влияют только на дочернее свойство.

перед вводом во второе текстовое поле (т. е., прежде чем свойство будет изменено в дочернем элементе), дочерняя/закодированная область находит name свойство в родительской области через прототипное наследование (пунктирная линия на рисунке ниже). Вот почему два текстовых поля изначально остаются синхронизированными. Ниже, если вы введете "Mark" в первое текстовое поле, это то, как выглядят области:

transcluded scope follows inheritance chain

Я создал скрипка где вы можете изучить две области. Нажмите на ссылку "Показать область" рядом с второе текстовое поле перед вводом во второе текстовое поле. Это позволит вам увидеть трансцендентную дочернюю область. Вы заметите, что он не имеет name собственность на данный момент. Очистите консоль, введите во второе текстовое поле и снова щелкните ссылку. Вы заметите, что дочерняя область теперь имеет name свойство, а начальным значением было значение, которое имело родительское свойство ("Mark"). Если вы набрали "likes Angular" во втором текстовом поле, это то, что выглядят области например:

transcluded primitive hides parent property

есть два решения:

  1. сделайте то, что предлагает @pgreen2 (это "лучшее решение") - используйте объект вместо примитива. При использовании объекта дочерняя / закодированная область не получает новое свойство. Здесь действует только прототипное наследование. На рисунке, приведенном ниже, предполагается, что $охвата editCtrl имеет этот объект определен:
    $scope.myObject = { name: "Mark", anotherProp: ... }:
    object in parent
  2. использовать $parent в дочерняя область (это хрупкое решение и не рекомендуется, поскольку оно делает предположения о структуре HTML): use ng-model="$parent.name" внутри , который находится в элементе . На первом рисунке выше показано, как это работает.

синтаксическая ошибка возникает при использовании scope: {name: '='} потому что при использовании двусторонней привязки данных (т. е. при использовании'=') интерполяция не допускается-т. е. {{}} не может использоваться. Вместо <tabs name="{{name}}"> использовать <tabs name="name">.

С помощью '@' работает так же, как случай transclude, потому что ng-transclude использует область transcluded, а не изолированную область, созданную с помощью scope: { ... }.

для получения (много) дополнительной информации об областях (включая фотографии) см.
каковы нюансы области прототипного / прототипического наследования в AngularJS?

Я считаю, что проблема связана с масштабированием. Изначально внутреннее текстовое поле не имеет name set, поэтому он наследуется от внешней области. Вот почему ввод текста во внешнем поле отражается во внутреннем поле. Однако после ввода во внутреннем поле происходит, внутренняя область теперь содержит name Что означает, что он больше не привязан к внешнему name поэтому внешнее поле не синхронизировать.

соответствующий способ исправить только хранение моделей в области, а не ваш ценности. Я исправил это в http://jsfiddle.net/pdgreen/5RVza/ фокус в том, чтобы создать объект модели (data) и ссылки на значения на нем.

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

Я считаю, что, как сказал Мишко Хевери, объем должен быть только для записи в контроллере, и только для чтения в директивах.

обновление: Ссылка:https://www.youtube.com/watch?v=ZhfUv0spHCY#t=29m19s

синтаксическая ошибка означает, что вы что-то неправильно написали. Это не связано с конкретной структурой / библиотекой. Вы, вероятно, забыли добавить ", " или закрыть парантез. Проверьте это снова