用ng-transclude在指令中重复ng-repeat

我想在内容发生变化时创build一个包含自定义行为的列表。 我尝试为此创build一个指令,但是如何将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:

 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; }); } } }); 

我在这里有一部分工作

编辑:

标准:

  • 该项目的模板必须在视图中定义,而不是在指令中定义,并且必须有权访问子范围内的项目属性。 理想情况下,我想要像在ng-repeat指令中那样定义它
  • 该指令必须有权访问列表,以便我可以设置正确的手表并更改内容。 如果可能的话,我希望能够轻松访问生成的DOM项目(我也可以使用element[0].querySelectorAll('ul>li')或其他东西,它只需要在Chrome上工作)。
  • 如果可能的话,我想重复使用ng-repeat指令中的逻辑,因为它已经做了很多我想要的。 我最好不要复制代码。 我只是想扩大它的行为,而不是改变它

自己解决了这个问题:

我可以在编译步骤( jsfiddle )中通过在编译模板时添加ng-repeat属性并将其提供给我的属性的内容来完成。

HTML:

 <div ng-app="myApp"> <div ng-controller="ctrl"> <mylist element="myItem in items">{{myItem}}</mylist> </div> </div> 

使用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); } } } }); 

另一种方法来达到这一点如下。

index.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:

 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(); }); }); } }; }); 

看到它在这个类似的重击者的行动。 基于这个长期的Github问题的讨论 。

其他答案不幸的是不适用于最新版本的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> 

Transcluding不是必需的,因为items包含我们需要呈现模板的内容。 换句话说,元素内部没有任何东西 – 即, <mylist>nothing new here we need to transclude</mylist> 。 看来Angular也会为我们做$监视。

 .directive('mylist', function () { return { restrict:'E', replace: true, scope: true, template: [ '<ul>', '<li ng-repeat="myItem in items">{{myItem}}</li>', '</ul>' ].join('') } }); 

HTML:

 <mylist></mylist> 

小提琴 。

请注意,创build一个新的作用域是可选的,所以你可以注释掉这一行:

 //scope: true, 

更新 :您可以select创build一个隔离范围:

 scope: { items: '='}, 

HTML:

 <mylist items=items></mylist> 

小提琴 。

Update2 :根据Jan提供的其他信息:

项目的模板必须在视图中定义…我想重复使用ng-repeat指令中的逻辑

好吧,让我们把它放在视图中,并使用ng-repeat:

 <ul mylist> <li ng-repeat="myItem in items"> <span class="etc">{{myItem}}</span> </li> </ul> 

它[该指令]必须有权访问子范围内的项目属性…该指令必须有权访问该列表,以便我可以设置正确的手表并改变事物

继原来的小提琴之后,我们将使用一个正常的子范围(即,子范围将原型从父范围inheritance): scope: true, 这将确保指令可以访问在控制器范围上定义的属性,例如items

访问生成的DOM项目

指令的链接函数有一个element参数。 所以在上面的HTML中,元素将被设置为<ul>元素。 所以我们可以访问所有的DOM元素。 例如, element.find('li')element.children() 。 在下面引用的小提琴中,我有$看items数组。 $ watchcallback可以访问element ,所以你可以访问生成的DOM项目。 callback日志element.children()到控制台。

小提琴 。

总之,要将自定义行为添加到列表中,只需将指令放在ul或ol上即可。