将数据传递给嵌入元素 [英] Pass data to transcluded element
问题描述
我想创建一个指令来组织按日期分组的显示数据.我还希望能够指定一个指令来显示各个行.在一个完美的世界里,它看起来像这样(但又漂亮又漂亮)
10 月 28 日,星期五[一些指令 html][一些指令 html][一些指令 html]10 月 29 日星期六[一些指令 html]10 月 30 日,星期日[一些指令 html][一些指令 html]...
这显然行不通,所以如果您有更好的方法请告诉我,但我希望能够按照以下方式做一些事情:
app.directive('dateOrganized', [function(){返回 {模板:''+'<div ng-repeat="organizedDate in OrganizedDate">'+'{{organizedDate.date |日期}}'+'<div ng-repeat="组织日期中的项目.items">'+'{{rowDirectiveHtml}}' +'</div>'+'</div>'+'</div>',范围: {有组织的日期:'=',rowDirectiveHtml: '='}...};}])app.directive('itemRow', [function(){返回 {模板:'<div>{{item.data}}</div>',范围: {项目:'='}};}]);
然后像这样使用它:
<div data-organized Organized-dates="stuff" row-directive-html="<div item-row item=\"item\"/>"/>
我知道这非常难看(并且不起作用,但我确信我可以通过一些调整让它工作)所以我真正想问的是,有没有更好的方法来做到这一点?
解决方案 这个问题比看起来更复杂,让我们分解一下.
您正在构建的是一个接受部分模板的指令 - <div item-row item="item"/>
- and 该模板使用(或与作用域链接)一个内部变量 - item
- 用户未在外部作用域中定义;它的含义由您的指令定义,您的用户通过阅读您的指令文档来发现"它.我通常用前缀 $
命名这样的魔法"变量,例如$item
.
第一步
不是通过属性绑定将模板作为 HTML-as-string 传递,而是将其作为内容传递并嵌入该内容.嵌入允许您将嵌入的内容绑定到任意范围:
<div>我的项目是:{{$item}}</div></foo>
.directive("foo", function(){返回 {范围: {},转置:真实,模板: "我是 foo
",链接:函数(范围,元素,属性,ctrls,transclude){scope.$item = "魔法变量";transclude(范围,功能(克隆内容){element.find("placeholder").replaceWith(clonedContent);});}};});
上面将放置模板 <div>my item is: {{$item}}</div>
(可以是您指定的任何模板)其中指令 foo
决定,并将链接到定义了 $item
的范围.
第 2 步
但是你的指令增加的复杂性是它使用ng-repeat
,它本身接受一个模板,你的指令接收的模板需要用作ng的模板-重复
.
仅使用上述方法,这是行不通的,因为在 link
运行时,ng-repeat
将在您有机会之前就已经嵌入了它自己的内容应用你的.
解决这个问题的一种方法是手动$compile
foo
的模板,而不是使用template
属性.在编译之前,我们将有机会将预期的模板放置在需要的地方:
.directive("foo", function($compile){返回 {范围: {},转置:真实,链接:函数(范围,元素,属性,ctrls,transclude){scope.items = [1, 2, 3, 4];var template = '<h1>我是foo</h1>\<div ng-repeat="$item in items">\<占位符></占位符>\
';var templateEl = angular.element(template);transclude(范围,功能(克隆内容){templateEl.find("placeholder").replaceWith(clonedContent);$compile(templateEl)(scope, function(clonedTemplate){element.append(clonedTemplate);});});}};});
I want to create a directive that organizes a displays data grouped by date. I also want to be able to specify a directive that will display the individual rows. In a perfect world it would look something like this (but nice and pretty)
Friday, Oct 28
[some directive html]
[some directive html]
[some directive html]
Saturday, Oct 29
[some directive html]
Sunday, Oct 30
[some directive html]
[some directive html]
...
This obviously doesn't work, so if you have a better approach please tell me, but I was hoping to be able to do something along these lines:
app.directive('dateOrganized', [function(){
return {
template:
'<div>' +
'<div ng-repeat="organizedDate in organizedDate">' +
'<div>{{organizedDate.date | date}}</div>' +
'<div ng-repeat="item in organizedDate.items">' +
'{{rowDirectiveHtml}}' +
'</div>' +
'</div>' +
'</div>',
scope: {
organizedDates: '=',
rowDirectiveHtml: '='
}
...
};
}])
app.directive('itemRow', [function(){
return {
template: '<div>{{item.data}}</div>',
scope: {
item: '='
}
};
}]);
then use it like this:
<div data-organized organized-dates="stuff" row-directive-html="<div item-row item=\"item\" />" />
I know this is super ugly (and doesn't work, but I'm sure I could get it working with a few tweaks) so what I am really asking, is there a better way to do this?
This question is more complicated than might appear, so let's break it down.
What you are building is a directive that accepts a partial template - <div item-row item="item" />
- and that template uses (or linked against a scope with) an inner variable - item
- that is not defined in the outer scope by the user; its meaning is defined by your directive and your user "discovers" it by reading the documentation of your directive. I typically name such "magic" variables with a prefixed $
, e.g. $item
.
Step 1
Instead of passing a template as an HTML-as-string via attribute binding, pass it as contents and transclude that content. Transcluding allows you to bind the transcluded content against an arbitrary scope:
<foo>
<div>my item is: {{$item}}</div>
</foo>
.directive("foo", function(){
return {
scope: {},
transclude: true,
template: "<h1>I am foo</h1><placeholder></placeholder>",
link: function(scope, element, attrs, ctrls, transclude){
scope.$item = "magic variable";
transclude(scope, function(clonedContent){
element.find("placeholder").replaceWith(clonedContent);
});
}
};
});
The above will place the template <div>my item is: {{$item}}</div>
(could be any template you specify) where the directive foo
decides, and will link against a scope that has $item
defined.
Step 2
But the added complexity of your directive is that it uses ng-repeat
, which by itself accepts a template, and the template your directive receives needs to be used as a template of ng-repeat
.
With just the approach above, this would not work, since by the time link
runs, ng-repeat
will have already transcluded its own content before you had a chance to apply yours.
One way to address that is to manually $compile
the template of foo
instead of using the template
property. Prior to compiling, we will have a chance to place the intended template where needed:
.directive("foo", function($compile){
return {
scope: {},
transclude: true,
link: function(scope, element, attrs, ctrls, transclude){
scope.items = [1, 2, 3, 4];
var template = '<h1>I am foo</h1>\
<div ng-repeat="$item in items">\
<placeholder></placeholder>\
</div>';
var templateEl = angular.element(template);
transclude(scope, function(clonedContent){
templateEl.find("placeholder").replaceWith(clonedContent);
$compile(templateEl)(scope, function(clonedTemplate){
element.append(clonedTemplate);
});
});
}
};
});
这篇关于将数据传递给嵌入元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!