Transclude element в директивах AngularJS

transclude: ‘element’ - трансклюдирует весь элемент, и функция связывания трансклюда вводится в функцию компиляции. Вы не можете иметь доступ к области здесь, потому что область еще не создана. Compile function создает функцию связи для директивы, которая имеет доступ к scope, и transcludeFn позволяет коснуться клонированного элемента (который был трансклинирован) для манипуляции DOM или использовать данные, привязанные к scope в нем. Для вашего сведения, это используется в ng-repeat и NG-switch.

Попробуем разобраться что такое transclude: element и как оборачивать директиву в дополнительную разметку.

Для начала создадим форму.

<form>
    <div>
      <label>Email:</label>
      <input type="text" placeholder="Place your email">
    </div>
    <div>
      <label>Password:</label>
      <input type="password" placeholder="Enter your password">
    </div>
    <button>Submit</button>
</form>

Мы будем добавлять директиву на елемент формы. Назовем директиву wrap-in и передадим параметр red.

<form wrap-in="red">
</form>

Давайте создадим директиву

app.directive('wrapIn', function () {
  return {
    link: function (scope, element, attrs) {
      console.log('wrapIn');
    }
  };
});

В консоли у нас вывелось wrapIn.

Теперь добавим поле transclude.

app.directive('wrapIn', function () {
  return {
    transclude: 'element',
    link: function (scope, element, attrs) {
      console.log('wrapIn');
    }
  };
});

Как мы видим в браузере, весь контент у нас пропал. Давайте напишем текст до формы, чтобы видеть что происходит

My form here

Как мы видим, в инспекте елементов есть надпись «My form here», а дальше идет комментарий wrapIn: red. Когда мы используем transclude, то у нас остается такой комментарий, чтобы было видно, что здесь должна применятся директива.

Теперь давайте передадим в линк функцию пятым параметром transclude.

link: function (scope, element, attrs, ctrl, transclude) {
  console.log('wrapIn');
  transclude(scope, function (clone) {
    console.log(clone);
  });
}

Мы вызвали функцию transclude с пераметром scope и функцией, где первым параметром идет clone.

В консоли мы видим,что clone - это наша форма, которая сейчас находится в transclude. Как мы видим, это полностью наш елемент, на который мы повесили директиву. Не его содержимое, а весь елемент. Это главное отличие transclude = element от transclude = true.

Теперь давайте создадим в body script.

<script type="text/ng-template" id="red">
  <div class="red"></div>
</script>

Зачем мы это делаем? Мы хотим потом в директиве из templateCache достать шаблон.

Теперь давайте в нашу директиву заинджектим templateCache.

app.directive('wrapIn', function () {
  return {
    transclude: 'element',
    link: function (scope, element, attrs, ctrl, transclude) {
      var template = $templateCache.get(attrs.wrapIn);
      console.log('wrapIn', template);
      transclude(scope, function (clone) {
        console.log(clone);
      });
    }
  };
});

attrs.wrapIn берет аргумент директивы wrap-in и означает red. В скрипте у нас id нашего шаблона red. То есть мы хотим из templateCache выгрести шаблон с id red.
В консоли вывелся наш шаблон.

Теперь мы хотим сгенерировать новый елемент.

app.directive('wrapIn', function ($templateCache) {
  return {
    transclude: 'element',
    link: function (scope, element, attrs, ctrl, transclude) {
      var template = $templateCache.get(attrs.wrapIn);
      var templateElement = angular.element(template);
      transclude(scope, function (clone) {
        element.after(templateElement.append(clone));
      });
    }
  };
});

В templateElement будет уже записан не string, а созданный DOM елемент. Далее в наш елемент, который пустой, так как срабатывает transclude добавляем templateElement и внутрь аппендим clone. clone как вы помните - это наша форма.
В браузере мы видим, что наша форма выводится. И мы видим, что у нас красные стили из-за того, что все форма обернута в класс red.

Так очень легко оборачивать директиву в какой-то нужный вам контент. Давайте создадим еще один скрипт с id=table.
В директиве wrap-in передадим параметр table.

<!DOCTYPE html>
<html lang="en" ng-app='app'>
<head>
    <meta charset="UTF-8">
    <title></title>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.9/angular.min.js"></script>
    <style type="text/css">
        .red{
            color: red;
        }
    </style>
  <script>
        var app = angular.module('app', []);
 
        app.directive('wrapIn', function ($templateCache) {
            return {
                transclude: 'element',
                link: function (scope, element, attrs, ctrl, transclude) {
                    var template = $templateCache.get(attrs.wrapIn);
                    var templateElement = angular.element(template);
                    transclude(scope, function (clone) {
                        element.after(templateElement.append(clone));
                    });
                }
            };
        });
    </script>
</head>
<body>
<script type="text/ng-template" id="red">
    <div class="red"></div>
</script>
<script type="text/ng-template" id="table">
    <div>Header for table</div>
</script>
<form wrap-in="table">
    <div>
        <label>Email:</label>
        <input type="text" placeholder="Place your email">
    </div>
    <div>
        <label>Password:</label>
        <input type="password" placeholder="Enter your password">
    </div>
    <button>Submit</button>
</form>
</body>
</html>

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

Главное, что вы должны заполнить из этого видео, что transclude = element позволяет делать transclude всей директивы сразу. Не содержимого блока, а всего блока.