Можно ли использовать $$ prevSibling для доступа к данным области в директиве 'transcluded'?

Моя настройка директивы выглядит следующим образом:

<div data-directive-a data-value="#33ff33" data-checked="true">
  <div data-directive-b></div>
</div>
  • Я использую transclusion для обеспечения отображения directiveB.
  • directiveA имеет флажок, который предназначен для изменения некоторого значения, когда он установлен.
  • это значение должно быть доступно в области directiveA и directiveB.

Мне удалось это сделать, но только путем ссылки $$prevSibling - есть ли лучший способ?

Вот код: http://jsfiddle.net/janeklb/yugQf/ (в этом примере щелчок по флажку просто означает "очистить" значение)

-

Немного больше глубины: "Содержимое" directiveA (то, что переводится в него) не всегда directiveB. Другие directiveB -подобные директивы также окажутся там. Типы directiveB "всегда будут использоваться в пределах directiveA.

Ответ 1

Чтобы избежать слишком сложного соединения компонентов, я бы не использовал $$prevSibling. Лучшее решение, так как ваши компоненты directiveB, как ожидается, будут использоваться в directiveA компонентах, должны использовать require.

.directive( 'directiveB', function () {
  return {
    require: '^directiveA',
    scope: true,
    link: function ( scope, element, attrs, directiveA ) {
      scope.obj = directiveA.getObj();
    }
  };
})

^require указывает, что где-то на элементе этой директивы или на любом элементе выше в иерархии DOM есть директива, называемая directiveA, и мы хотим вызвать методы на ее контроллере.

.directive( 'directiveA', function () {
  return {
    // ...
    controller: function ( $scope ) {
      // ...
      this.getObj = function () {
        return $scope.obj;
      };
    }
  };
})

Итак, теперь в directiveB вы можете использовать ng-model="obj.attr".

В этом есть много вариаций, но, учитывая общий вопрос, я считаю, что это лучший подход. Здесь обновлен Fiddle: http://jsfiddle.net/yugQf/7/.

Ответ 2

@Джош упомянул в своем ответе, что

Лучшее решение, так как ваши компоненты directiveB, как ожидается, будут использоваться в составе directiveA, должны использовать require.

Я играл с этим, и я считаю, что контроллер на directiveA является единственным решением (так +1 Джош). Вот как выглядят области с помощью скрипта OP: scopes picture

(Переверните коричневую стрелку, и у вас есть $$ previousSibling вместо $$ nextSibling.)

Кроме $$previousSibling, область 004 не имеет пути для выделения области 003. Обратите внимание, что область 004 представляет собой выделенную область, которую создает directiveA, и поскольку directiveB не создает новую область, эта область также используется на directiveB.

Поскольку объект, который вы хотите использовать с directiveB, создается в контроллере directiveA, мы также не можем использовать атрибуты для обмена данными между директивами.


Создание модели внутри директивы, а затем разделение этой модели на внешний мир довольно нетипично. Как правило, вы хотите определить свои модели вне своих директив и даже вне своих контроллеров (прослушать несколько минут до Misko). Услуги часто являются хорошим местом для хранения ваших моделей/данных. Контроллеры должны обычно ссылаться на части модели (моделей), которые необходимо проецировать в представление, с которым они связаны.

Для простоты я собираюсь определить модель на контроллере, тогда директивы оба будут обращаться к этой модели обычным способом. Для педагогических целей directiveA по-прежнему будет использовать область выделения, а directiveB создаст новую область с помощью scope: new, как в ответе @Josh. Но любой тип (изолировать, новый ребенок, новая область) и комбинация будут работать, теперь, когда у нас есть модель, определенная в родительской области.

Ctrl

$scope.model = {value: '#33ff33', checkedState = true};

HTML

<div ng-controller="NoTouchPrevSibling">
   <div data-directive-a data-value="model.value" data-checked="model.checkedState">
      <div data-directive-b></div>
   </div>

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

директивы

app.directive('directiveA', function () {
    return {
        template: '<div>' 
            + 'inside parent directive: {{checkedState}}'
            + '<input type="checkbox" ng-model="checkedState" />'
            + '<div ng-transclude></div>'
            + '</div>',
        transclude: true,
        replace: true,
        scope: {
              value: '=',
              checkedState: '=checked'
            },
    };
});
app.directive('directiveB', function () {
    return {
        template: '<div>' 
            + '<span>inside transcluded directive: {{model.checkedState}}</span>'
            + '<input type="text" ng-model="model.value" />'
            + '</div>',
        replace: true,
        scope: true
    };
});

Прицелы

scopes

Обратите внимание, что область childiveiveiveB (006) наследуется от директивы Transcluded scope (005).

После того, как вы установите флажок и измените значение в текстовом поле:

scopes after interaction

Обратите внимание, что Angular обрабатывает обновление свойств области изоляции. Нормальное прототипное наследование JavaScript предоставляет доступ к объекту childiveiveiveB к model в области контроллера (003).

Fiddle