Alb*_*orz 22 angularjs angularjs-directive
我有两个指令
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: "<div ng-transclude ></div>",
link: function (scope, elem, attrs) {
}
}
});
Run Code Online (Sandbox Code Playgroud)
这是我的观点:
<panel1>
<panel>
<input type="text" ng-model="firstName" />
</panel>
</panel1>
Run Code Online (Sandbox Code Playgroud)
错误:[ngTransclude:orphan]在模板中非法使用ngTransclude指令!没有找到需要转换的父指令.元件:<div class="ng-scope" ng-transclude="">
我知道panel1不是一个实用的指令.但在我的实际应用程序中,我也遇到了这个问题.
我在http://docs.angularjs.org/error/ngTransclude:orphan上看到了一些解释.但是不知道为什么我在这里有这个错误以及如何解决它.
编辑 我创建了一个jsfiddle页面.先感谢您.
编辑
I my real app panel1 does something like this:
<panel1>
<input type="text>
<input type="text>
<!--other elements or directive-->
</panel1>
Run Code Online (Sandbox Code Playgroud)
result =>
<div>
<div class="x"><input type="text></div>
<div class="x"><input type="text></div>
<!--other elements or directive wrapped in div -->
</div>
Run Code Online (Sandbox Code Playgroud)
Kha*_* TO 36
原因是当DOM完成加载时,angular将遍历DOM并在调用编译和链接函数之前将所有指令转换为其模板.
这意味着当你打电话时$compile(clone.children()[0])(scope),clone.children()[0]你<panel>在这种情况下的那个已经被角度变换了.
clone.children()已成为:
<div ng-transclude="">fsafsafasdf</div>
(面板元件已被移除并更换).
你正在用一个正常的div编译它也是一样的ng-transclude.当你编译一个普通的div时ng-transclude,angular会抛出异常,如文档中所述:
当您忘记在某些指令定义中设置transclude:true,然后在指令的模板中使用ngTransclude时,通常会发生此错误.
DEMO(检查控制台看输出)
即使你设置replace:false保留你的<panel>,有时你会看到这样的变换元素:
<panel class="ng-scope"><div ng-transclude=""><div ng-transclude="" class="ng-scope"><div ng-transclude="" class="ng-scope">fsafsafasdf</div></div></div></panel>
这也是有问题的,因为它ng-transclude是重复的
为避免与角度编译过程冲突,我建议将内部html设置<panel1>为template或templateUrl属性
你的HTML:
<div data-ng-app="app">
<panel1>
</panel1>
</div>
Run Code Online (Sandbox Code Playgroud)
你的JS:
app.directive('panel1', function ($compile) {
return {
restrict: "E",
template:"<panel><input type='text' ng-model='firstName'>{{firstName}}</panel>",
}
});
Run Code Online (Sandbox Code Playgroud)
如您所见,此代码更清晰,因为我们不需要手动转换元素.
更新了一个动态添加元素的解决方案,无需使用模板或templateUrl:
app.directive('panel1', function ($compile) {
return {
restrict: "E",
template:"<div></div>",
link : function(scope,element){
var html = "<panel><input type='text' ng-model='firstName'>{{firstName}}</panel>";
element.append(html);
$compile(element.contents())(scope);
}
}
});
Run Code Online (Sandbox Code Playgroud)
如果你想把它放在html页面上,请确保不要再次编译它:
如果你需要为每个孩子添加一个div.只需使用开箱即用ng-transclude.
app.directive('panel1', function ($compile) {
return {
restrict: "E",
replace:true,
transclude: true,
template:"<div><div ng-transclude></div></div>" //you could adjust your template to add more nesting divs or remove
}
});
Run Code Online (Sandbox Code Playgroud)
演示(您可能需要根据需要调整模板,删除div或添加更多div)
基于OP更新问题的解决方案:
app.directive('panel1', function ($compile) {
return {
restrict: "E",
replace:true,
transclude: true,
template:"<div ng-transclude></div>",
link: function (scope, elem, attrs) {
elem.children().wrap("<div>"); //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
}
}
});
Run Code Online (Sandbox Code Playgroud)
您在代码中做了一些错误.我会尝试列出它们:
首先,由于您使用的是角度1.2.6,因此您不应再使用transclude(链接器函数)作为编译函数的参数.这已被弃用,现在应作为第5个参数传递给您的链接函数:
compile: function (element, attr) {
return function (scope, element, attr, ctrl, linker) {
....};
Run Code Online (Sandbox Code Playgroud)
这不会导致您遇到的特定问题,但停止使用已弃用的语法是一种很好的做法.
真正的问题在于如何在panel1指令中应用transclude函数:
parent.prepend($compile(clone.children()[0])(scope));
Run Code Online (Sandbox Code Playgroud)
在我发现错误之前,让我们快速回顾一下transclude的工作原理.
只要指令使用了转义,就会从dom中删除已转换的内容.但是它的编译内容可以通过作为链接函数的第5个参数(通常称为transclude函数)传入的函数来访问.
关键是内容是编译的.这意味着你不应该在传递给你的transclude的dom上调用$ compile.
此外,当您尝试插入已转换的DOM时,您将转到父级并尝试将其添加到其中.通常,指令应该将它们的dom操作限制在它们自己的元素和下面,而不是尝试修改父dom.这可以极大地混淆按顺序和层次遍历DOM的角度.
从你想要做的事情来看,更容易实现它的方法是使用transclude: true而不是transclude: 'element'.让我们解释一下差异:
transclude: 'element' 将从DOM中删除元素本身,并在调用transclude函数时返回整个元素.
transclude: true 只会从dom中移除元素的子元素,并在调用transclude时将孩子们带回来.
既然你似乎只关心孩子,你应该使用transclude true(而不是从你的克隆中获取children()).然后你可以简单地用它的子元素替换元素(因此不会上升和弄乱父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);
});
}
}
});
Run Code Online (Sandbox Code Playgroud)
忽略上一个回答中关于replace: true和所说的内容transclude: true.这不是事情的工作方式,你的面板指令很好,只要你修复你的panel1指令就应该按预期工作.
这是我所做的更正的js-fiddle,希望它能像你期望的那样工作.
编辑:
有人问你是否可以将被抄送的内容包装在一个div中.最简单的方法是简单地使用像你在其他指令中那样的模板(模板中的id就是这样你可以在html中看到它,它没有其他用途):
app.directive('panel1', function ($compile) {
return {
restrict: "E",
transclude: true,
replace: true,
template: "<div id='wrappingDiv' ng-transclude></div>"
}
});
Run Code Online (Sandbox Code Playgroud)
或者如果你想使用transclude功能(我的个人偏好):
app.directive('panel1', function ($compile) {
return {
restrict: "E",
transclude: true,
replace: true,
template: "<div id='wrappingDiv'></div>",
link: function (scope, element, attr, ctrl, linker) {
linker(function (clone) {
element.append(clone);
});
}
}
});
Run Code Online (Sandbox Code Playgroud)
我更喜欢这种语法的原因是这ng-transclude是一个容易混淆的简单而愚蠢的指令.虽然在这种情况下很简单,但手动将dom准确地添加到您想要的位置是一种故障安全的方法.
这是它的小提琴:
| 归档时间: |
|
| 查看次数: |
31172 次 |
| 最近记录: |