Javascript 在指令中使用 ng-transclude 进行 ng-repeat
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14388247/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ng-repeat with ng-transclude inside a directive
提问by Jan
I want to create a list with custom behavior when it's content changes. I try to create a directive for this but I get a bit lost with how to combine the ng-transclude with the ng-repeat directive. Can somebody put me on track?
我想在内容更改时创建一个具有自定义行为的列表。我尝试为此创建一个指令,但我对如何将 ng-transclude 与 ng-repeat 指令结合起来有点迷茫。有人可以让我走上正轨吗?
Html:
网址:
<div ng-app="myApp">
<div ng-controller="ctrl">
<mylist items="myItem in items">
<span class="etc">{{myItem}}</span>
</mylist>
</div>
</div>
Javascript:
Javascript:
angular.module('myApp', [])
.controller('ctrl', function ($scope) {
$scope.items = ['one', 'two', 'three'];
})
.directive('mylist', function () {
return {
restrict:'E',
transclude: 'element',
replace: true,
scope: true,
template: [
'<ul>',
'<li ng-repeat="WhatGoesHere in items" ng-transclude></li>',
'</ul>'
].join(''),
link: function (scope, element, attr) {
var parts = attr.items.split(' in ');
var itemPart = parts[0];
var itemsPart = parts[1];
scope.$watch(itemsPart, function (value) {
scope.items = value;
});
}
}
});
I've got part of this somewhat working here
我有一部分在这里工作
EDIT:
编辑:
Criteria:
标准:
- The template of the item must be defined in the view, not in the directive and it must have access to an item property in a child scope. Ideally I want to define this like it is done in the ng-repeat directive
- The directive must have access to the list so I can set proper watches and change things. If possible I would like to have easy access to the generated DOM items (I can also do it with
element[0].querySelectorAll('ul>li')or something, It only has to work on Chrome). - If possible I would like to reuse the logic in the ng-repeat directive because it does already do a lot of what I want. Preferably I don't want to copy the code. I just want to augment its behavior, not change it
- 项目的模板必须在视图中定义,而不是在指令中定义,并且它必须能够访问子作用域中的项目属性。理想情况下,我想像在 ng-repeat 指令中那样定义它
- 该指令必须有权访问该列表,以便我可以设置适当的监视和更改内容。如果可能的话,我希望能够轻松访问生成的 DOM 项目(我也可以用它
element[0].querySelectorAll('ul>li')或其他东西来做,它只需要在 Chrome 上工作)。 - 如果可能的话,我想重用 ng-repeat 指令中的逻辑,因为它已经做了很多我想要的。最好我不想复制代码。我只想增强它的行为,而不是改变它
采纳答案by Jan
Solved the problem myself:
自己解决了这个问题:
I am able to do it in the compile step (jsfiddle) by adding the ng-repeatattribute when the template is compiled and feeding it the content of my attribute.
我可以在编译步骤 ( jsfiddle) 中通过在编译ng-repeat模板时添加属性并将我的属性内容提供给它来完成。
Html:
网址:
<div ng-app="myApp">
<div ng-controller="ctrl">
<mylist element="myItem in items">{{myItem}}</mylist>
</div>
</div>
Javascript:
Javascript:
var myApp = angular.module('myApp', [])
.controller('ctrl', function ($scope) {
$scope.items = ['one', 'two', 'three'];
})
.directive('mylist', function ($parse) {
return {
restrict:'E',
transclude: 'element',
replace: true,
scope: true,
template: [
'<ul>',
'<li ng-transclude></li>',
'</ul>'
].join(''),
compile: function (tElement, tAttrs, transclude) {
var rpt = document.createAttribute('ng-repeat');
rpt.nodeValue = tAttrs.element;
tElement[0].children[0].attributes.setNamedItem(rpt);
return function (scope, element, attr) {
var rhs = attr.element.split(' in ')[1];
scope.items = $parse(rhs)(scope);
console.log(scope.items);
}
}
}
});
回答by Izhaki
An alternative way to achieve this as follows.
实现此目的的替代方法如下。
Index.html:
索引.html:
<html ng-app='myApp'>
<head>
<title>AngularJS Transclude within Repeat Within Directive</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.min.js"></script>
<script src='index.js'></script>
</head>
<body ng-controller='myController'>
<people>Hello {{person.name}}</people>
<button name="button" ng-click="changeRob()">Change Rob</button>
</body>
</html>
index.js:
索引.js:
var myApp = angular.module( 'myApp', [] );
myApp.controller( 'myController', function( $scope ) {
$scope.people = [
{ name: 'Rob' },
{ name: 'Alex' },
{ name: 'John' }
];
$scope.changeRob = function() {
$scope.people[0].name = 'Lowe';
}
});
myApp.directive( 'people', function() {
return {
restrict: 'E',
transclude: true,
template: '<div ng-repeat="person in people" transcope></div>',
}
});
myApp.directive( 'transcope', function() {
return {
link: function( $scope, $element, $attrs, controller, $transclude ) {
if ( !$transclude ) {
throw minErr( 'ngTransclude' )( 'orphan',
'Illegal use of ngTransclude directive in the template! ' +
'No parent directive that requires a transclusion found. ' +
'Element: {0}',
startingTag( $element ));
}
var innerScope = $scope.$new();
$transclude( innerScope, function( clone ) {
$element.empty();
$element.append( clone );
$element.on( '$destroy', function() {
innerScope.$destroy();
});
});
}
};
});
See it in action in this similar plunker. Based on this long Github issue discussion.
在这个类似的 plunker 中查看它的运行情况。基于这个漫长的 Github 问题讨论。
回答by Morteza Tourani
Other answers unfortunatelydoes not work with newest version of angular(I checked 1.4) so I think there is a benefit to share this jsbinI found:
不幸的是,其他答案不适用于最新版本的 angular(我检查过1.4),所以我认为分享我发现的这个 jsbin 有好处:
var app = angular.module('app', [])
.controller('TestCtrl', function($scope) {
$scope.myRecords = ['foo', 'bar', 'baz'];
});
app.directive('myDirective', function($compile) {
var template = '<div id="inner-transclude" ng-repeat="record in records"></div>';
return {
scope: {
records: '='
},
restrict: 'A',
compile: function(ele) {
var transclude = ele.html();
ele.html('');
return function(scope, elem) {
var tpl = angular.element(template);
tpl.append(transclude);
$compile(tpl)(scope);
elem.append(tpl);
};
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.js"></script>
<div ng-app="app" ng-controller="TestCtrl">
<div my-directive records="myRecords">
?: {{record}}
</div>
</div>
回答by Mark Rajcok
Transcluding isn't necessary because itemscontains what we need to render the template. Put another way, there isn't anything inside the element -- i.e., <mylist>nothing new here we need to transclude</mylist>. It seems Angular will do the $watching for us too.
转置不是必需的,因为items包含我们渲染模板所需的内容。换句话说,元素内部没有任何东西——即 <mylist>nothing new here we need to transclude</mylist>. 似乎 Angular 也会为我们做 $watching。
.directive('mylist', function () {
return {
restrict:'E',
replace: true,
scope: true,
template: [
'<ul>',
'<li ng-repeat="myItem in items">{{myItem}}</li>',
'</ul>'
].join('')
}
});
HTML:
HTML:
<mylist></mylist>
小提琴。
Note that creating a new scope is optional, so you could comment out this line:
请注意,创建新范围是可选的,因此您可以注释掉这一行:
//scope: true,
Update: You could optionally create an isolate scope:
更新:您可以选择创建一个隔离范围:
scope: { items: '='},
HTML:
HTML:
<mylist items=items></mylist>
小提琴。
Update2: based on additional info provided by Jan:
更新 2:基于 Jan 提供的附加信息:
The template of the item must be defined in the view... I would like to reuse the logic in the ng-repeat directive
项目的模板必须在视图中定义...我想重用ng-repeat指令中的逻辑
Okay, so lets put it all in the view, and use ng-repeat:
好的,让我们把它全部放在视图中,并使用 ng-repeat:
<ul mylist>
<li ng-repeat="myItem in items">
<span class="etc">{{myItem}}</span>
</li>
</ul>
it [the directive] must have access to an item property in a child scope... The directive must have access to the list so I can set proper watches and change things
它 [指令] 必须有权访问子范围内的项目属性...该指令必须有权访问列表,以便我可以设置适当的监视并更改内容
Following your original fiddle, we'll use a normal child scope (i.e., the child scope will prototypically inherit from the parent scope): scope: true,. This will ensure the directive has access to the properties defined on the controller's scope, e.g., items.
遵循您的原始小提琴,我们将使用普通的子作用域(即子作用域将原型继承自父作用域):scope: true,。这将确保指令可以访问在控制器范围内定义的属性,例如,items。
access to the generated DOM items
访问生成的 DOM 项
The directive's link function has an elementargument. So in the HTML above, element will be set to the <ul>element. So we have access to all the DOM elements. E.g., element.find('li')or element.children(). In the fiddle referenced below, I have it $watch the itemsarray. The $watch callback has access to element, so you have access to the generated DOM items. The callback logs element.children()to the console.
指令的链接函数有一个element参数。所以在上面的 HTML 中, element 将被设置为<ul>元素。所以我们可以访问所有的 DOM 元素。例如,element.find('li')或element.children()。在下面引用的小提琴中,我有 $watchitems数组。$watch 回调可以访问element,因此您可以访问生成的 DOM 项。回调记录element.children()到控制台。
小提琴。
In summary, to add custom behavior to a list, just plop a directive onto a ul or ol and away you go.
总之,要将自定义行为添加到列表中,只需将指令放入 ul 或 ol 上即可。

