Недопустимое использование директивы ngTransclude в шаблоне

У меня есть две директивы

app.directive('panel1', function ($compile) { return { restrict: "E", transclude: 'element', compile: function (element, attr, linker) { return function (scope, element, attr) { var parent = element.parent(); linker(scope, function (clone) { parent.prepend($compile( clone.children()[0])(scope));//cause error. // parent.prepend(clone);// This line remove the error but i want to access the children in my real app. }); }; } } }); app.directive('panel', function ($compile) { return { restrict: "E", replace: true, transclude: true, template: "
", link: function (scope, elem, attrs) { } } });

И это мое мнение:

      

Ошибка: [ngTransclude: orphan] Незаконное использование директивы ngTransclude в шаблоне! Нет родительской директивы, для которой требуется переключение. Элемент:

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

Я вижу некоторые объяснения по http://docs.angularjs.org/error/ngTransclude:orphan . Но не знаю, почему у меня есть эта ошибка здесь и как ее решить.

EDIT Я создал страницу jsfiddle . Заранее спасибо.

РЕДАКТИРОВАТЬ

 I my real app panel1 does something like this:      

result =>

  

Причина в том, что DOM закончил загрузку, угловой будет проходить через DOM и преобразовать все директивы в свой шаблон перед вызовом функции компиляции и ссылки.

Это означает, что когда вы вызываете $compile(clone.children()[0])(scope) , clone.children()[0] который является вашей в этом случае уже преобразован угловым. clone.children() уже становится:

fsafsafasdf

(элемент панели был удален и заменен ).

То же самое происходит и с компиляцией нормального div с ng-transclude . Когда вы компилируете нормальный div с ng-transclude , исключение угловых исключений, как говорится в документах:

Эта ошибка часто возникает, когда вы забыли установить transclude: true в некоторых определениях директивы, а затем использовали ngTransclude в шаблоне директивы.

DEMO (проверить консоль, чтобы увидеть выход)

Даже если вы установите replace:false чтобы сохранить свою , иногда вы увидите преобразованный элемент следующим образом:

fsafsafasdf

что также проблематично, поскольку ng-transclude дублируется

DEMO

Чтобы избежать конфликта с процессом угловой компиляции , я рекомендую установить внутренний html как свойство template или templateUrl

Ваш HTML:

 

Ваш JS:

 app.directive('panel1', function ($compile) { return { restrict: "E", template:"{{firstName}}", } }); 

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

DEMO

Обновлено с помощью решения для динамического добавления элементов без использования шаблона или шаблонаUrl:

 app.directive('panel1', function ($compile) { return { restrict: "E", template:"
", link : function(scope,element){ var html = "{{firstName}}"; element.append(html); $compile(element.contents())(scope); } } });

DEMO

Если вы хотите разместить его на странице html, убедитесь, что вы не компилируете его снова:

DEMO

Если вам нужно добавить div для каждого ребенка. Просто используйте out-of the box ng-transclude .

 app.directive('panel1', function ($compile) { return { restrict: "E", replace:true, transclude: true, template:"
" //you could adjust your template to add more nesting divs or remove } });

DEMO (вам может потребоваться настроить шаблон в соответствии с вашими потребностями, удалить div или добавить еще div)

Решение основано на обновленном вопросе OP:

 app.directive('panel1', function ($compile) { return { restrict: "E", replace:true, transclude: true, template:"
", link: function (scope, elem, attrs) { elem.children().wrap("
"); //Don't need to use compile here. //Just wrap the children in a div, you could adjust this logic to add class to div depending on your children } } });

DEMO

Вы делаете несколько неправильных действий в своем коде. Я попытаюсь их перечислить:

Во-первых, поскольку вы используете угловой 1.2.6, вы больше не должны использовать transclude (функцию компоновщика) в качестве параметра функции компиляции. Это устарело и теперь должно быть передано в качестве 5-го параметра вашей функции связи:

 compile: function (element, attr) { return function (scope, element, attr, ctrl, linker) { ....}; 

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

Реальная проблема заключается в том, как вы применяете свою функцию panel1 директиве panel1 :

 parent.prepend($compile(clone.children()[0])(scope)); 

Прежде чем я пойду, что не так, давайте быстро рассмотрим, как работает transclude.

Всякий раз, когда в директиве используется переход, переводимое содержимое удаляется из dom. Но это скомпилированное содержимое доступно через функцию, переданную в качестве 5-го параметра вашей функции ссылок (обычно называемой функцией перехвата).

Ключ состоит в том, что контент компилируется . Это означает, что вы не должны вызывать $ compile на dom, переданном в ваш перехват.

Кроме того, когда вы пытаетесь вставить свою переведенную DOM, вы направляетесь к родительскому объекту и пытаетесь добавить его туда. Как правило, директивы должны ограничивать их манипуляции с dom до их собственного элемента и ниже, а не пытаться изменить родительский dom. Это может сильно запутать угловое устройство, которое перемещается по DOM по порядку и иерархически.

Судя по тому, что вы пытаетесь сделать, более простой способ сделать это – использовать transclude: true вместо transclude: 'element' . Давайте объясним разницу:

transclude: 'element' удалит сам элемент из DOM и вернет весь элемент обратно, когда вы вызовете функцию transclude.

transclude: true просто удалит дочерние элементы элемента из dom и вернет вам детей, когда вы позвоните в ваш перехват.

Поскольку вам кажется, что вы заботитесь только о детях, вы должны использовать transclude true (вместо того, чтобы получать детей () из вашего клона). Затем вы можете просто заменить элемент его дочерними элементами (поэтому не подходите и не возитесь с родительским dom).

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

Ваша окончательная упрощенная директива должна выглядеть примерно так:

  app.directive('panel1', function ($compile) { return { restrict: "E", transclude: true, link: function (scope, element, attr, ctrl, linker) { linker(function (clone) { element.replaceWith(clone); }); } } }); 

Игнорируйте сказанное в предыдущем ответе о replace: true и transclude: true . Дело не в том, как все работает, а в директиве вашей панели прекрасно и должно работать так, как вы ожидали, пока вы исправляете свою директиву panel1 .

Вот js-скрипт исправлений, которые я надеялся, что он работает так, как вы ожидаете.

http://jsfiddle.net/77Spt/3/

РЕДАКТИРОВАТЬ:

Было задано вопрос, можете ли вы перенести переведенный контент в div. Самый простой способ – просто использовать шаблон, как в своей другой директиве (идентификатор в шаблоне только для того, чтобы вы могли видеть его в html, он не служит никакой другой цели):

  app.directive('panel1', function ($compile) { return { restrict: "E", transclude: true, replace: true, template: "
" } });

Или если вы хотите использовать функцию transclude (мои личные предпочтения):

  app.directive('panel1', function ($compile) { return { restrict: "E", transclude: true, replace: true, template: "
", link: function (scope, element, attr, ctrl, linker) { linker(function (clone) { element.append(clone); }); } } });

Причина, по которой я предпочитаю этот синтаксис, заключается в том, что ng-transclude – простая и немая директива, которая легко путается. Хотя в этой ситуации это просто, ручное добавление dom точно в том месте, где вы хотите, – это безопасный способ сделать это.

Вот сценарий для него:

http://jsfiddle.net/77Spt/6/

Я получил это, потому что у меня была directiveChild вложенная в directiveParent в результате transclude .

Фокус в том, что directiveChild случайно использовал тот же templateUrl как directiveParent .

  • Директива AngularJS, связывающая функцию с несколькими аргументами
  • Как использовать функцию «replace» для пользовательских директив AngularJS?
  • Угловая: вызов функции controllerа внутри функции директивной ссылки с использованием &
  • Угловое название директивы: допускаются только строчные буквы?
  • Угловая JS: Какова потребность в функции ссылки директивы, когда у нас уже есть controller директивы с объемом?
  • Как установить фокус на поле ввода?
  • AngularJS: ngInclude vs директива
  • AngularJS - Динамическое создание элементов, определяющих директивы
  • Как понять «терминал» директивы?
  • вызов метода родительского controllerа из директивы в AngularJS
  • Понимание опции transclude определения директивы?
  • Давайте будем гением компьютера.