Принудительная область действия к именованному слоту с помощью ng-transclude

У меня есть следующая директива:

<selectable-item-list items="model.items">
    <item-template>
          <span ng-bind="item.text"></span>
    </item-template>
</selectable-item-list>

Проблема заключается в <item-template>, где item будет ссылкой на текущий итерируемый элемент, когда внутренний ng-repeat связан внутри <selectable-item-list>.

Насколько мне известно, кажется, что включения не могут видеть область действия директивы, поэтому item.text не может быть привязано, потому что item вообще нет.

Как бы вы решили этот сценарий? Раньше я вручную включал <item-template>, но у другого подхода были другие недостатки/проблемы.

Вот фрагмент исполняемого кода, который работает как образец моего реального случая:

var app = angular.module("app", []);

app.controller("some", function() {
  this.items = [{
    text: "hello"
  }, {
    text: "bye"
  }];
});

app.directive("test", function() {
  return {
    template: `<ol>
                  <li ng-repeat="item in items">
                      <div ng-transclude="itemTemplate"></div>
                  </li>
                </ol>`,
    transclude: {
      "itemTemplate": "itemTemplate"
    },
    scope: {
      "items": "="
    }
  }
});
<script src="https://code.angularjs.org/1.5.7/angular.js"></script>

<div ng-app="app" ng-controller="some as some">
  <test items="some.items">
    <item-template>
      <span ng-bind="item.text"></span>
    </item-template>
  </test>
</div>


person Matías Fidemraizer    schedule 08.07.2016    source источник
comment
Не могли бы вы показать нам упрощенную версию шаблонов selectable-item-list и item-template. Это может облегчить мою голову.   -  person Matt Lishman    schedule 08.07.2016
comment
Кроме того, вы правы насчет насколько я понимаю, кажется, что включения не могут видеть область действия директивы. Это делается для того, чтобы директивы не были связаны друг с другом, что противоречит цели включенных директив.   -  person Matt Lishman    schedule 08.07.2016
comment
@MattLishman Спасибо за ваши комментарии. Я постараюсь разместить здесь пример кода, чтобы мы могли вместе продолжить исследование. Я сделаю это через несколько часов .......   -  person Matías Fidemraizer    schedule 08.07.2016
comment
@MattLishman Наконец-то я сделал это за 5 минут. Проверьте мой обновленный вопрос, я добавил фрагмент исполняемого кода;)   -  person Matías Fidemraizer    schedule 08.07.2016
comment
@MattLishman Во всем фрагменте вы увидите, что оба элемента отображаются, но без содержимого, потому что, очевидно, item нельзя получить доступ из шаблона элемента....   -  person Matías Fidemraizer    schedule 08.07.2016
comment
@MattLishman Я сам мог бы найти окончательное решение! Смотрите мой добавленный ответ;)   -  person Matías Fidemraizer    schedule 08.07.2016


Ответы (1)


У меня было неправильное предположение! Когда я сказал, что включенный контент не может получить доступ к области действия директивы, я был неправ из-за этого другого вопроса и ответа: Почему область действия ng-transclude не является дочерней областью действия директивы, если директива имеет изолированную область действия? абсолютно устарело.

Фактически, есть еще один ответ как часть того же вопроса и ответа, где кто-то описал, что теперь это исправлено, и включенный контент может получить доступ к своему прямому область действия директивы с помощью $parent.

Поэтому я исправил свою проблему, просто заменив доступ к свойству item на $parent.item, и это сработало!

Я добавил рабочий фрагмент кода с этим исправлением:

var app = angular.module("app", []);

app.controller("some", function() {
  this.items = [{
    text: "hello"
  }, {
    text: "bye"
  }];
});

app.directive("test", function() {
  return {
    template: `<ol>
                  <li ng-repeat="item in items">
                      <div ng-transclude="itemTemplate"></div>
                  </li>
                </ol>`,
    transclude: {
      "itemTemplate": "itemTemplate"
    },
    scope: {
      "items": "="
    }
  }
});
<script src="https://code.angularjs.org/1.5.7/angular.js"></script>

<div ng-app="app" ng-controller="some as some">
  <test items="some.items">
    <item-template>
      <span ng-bind="$parent.item.text"></span>
    </item-template>
  </test>
</div>

person Matías Fidemraizer    schedule 08.07.2016
comment
Рад, что вы нашли рабочее решение. Я пытался найти сообщения SO, на которые вы ссылались ранее, но не смог. Однако я был бы осторожен, из этого поста в верхнем ответе хорошо сказано, что ng-transclude предназначен для того, чтобы директивы могли работать с произвольным контентом, а изолированные области предназначены для того, чтобы директивы могли инкапсулировать свои данные. Я был бы осторожен с тем, что теперь вы связали реализацию <item-template> с <test>, т.е. test всегда должен иметь доступный $parent.item.text. Если вы измените принцип работы test, вы также можете сломать item-templates. Хороший ответ, хотя. - person Matt Lishman; 08.07.2016
comment
@MattLishman Да, я знаю, что это связывает шаблон со всей директивой, но, в конце концов, это шаблон элемента, предназначенный для конкретной директивы. Со сцеплением здесь все в порядке, не так ли? - person Matías Fidemraizer; 09.07.2016
comment
Согласен, я думаю, если понятно, что они связаны, с этим нет проблем. Просто добавил это, чтобы другие думали об этом. Стоит отметить, что я думаю, что все области видимости имеют свойство $parent, так что эту технику можно использовать где угодно. - person Matt Lishman; 09.07.2016
comment
@MattLishman Да, ты прав. Кстати, благодаря этому подходу я могу удалить предыдущее чрезмерно разработанное решение, которое заключалось в ручной компиляции шаблона элемента с помощью $compile, и это имело много проблем. - person Matías Fidemraizer; 09.07.2016
comment
@MattLishman ИМХО Я считаю, что это недостаток дизайна в Angular 1.x, потому что было бы неплохо иметь что-то вроде этого: ‹div ng-transclude=itemTemplate scope=item›‹/div›, чтобы вы могли определить, что будет область во всем включенном контенте, и нам не нужны item.text или $parent.item.text, а просто text - person Matías Fidemraizer; 09.07.2016
comment
Да, согласен, было бы неплохо. Полагая это таким образом, кажется, что в angular 1 можно было бы сделать лучше. Я буду продолжать думать об этом. Сейчас 23:45, слишком поздно, чтобы думать об этом сейчас... ха - person Matt Lishman; 09.07.2016
comment
@MattLishman Лол, вот 13:46, что даже хуже, чем ты думаешь об этом ;D - person Matías Fidemraizer; 09.07.2016