我希望在内容更改时创建包含自定义行为的列表。我尝试为此创建一个指令但是我对如何将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;
});
}
}
});
我有一部分工作here
标准:
element[0].querySelectorAll('ul>li')
或其他东西,它只需要在Chrome上运行。)答案 0 :(得分:20)
自己解决了这个问题:
我可以在编译步骤(jsfiddle)中通过在编译模板时添加ng-repeat
属性并将其提供给我的属性内容来完成。
<div ng-app="myApp">
<div ng-controller="ctrl">
<mylist element="myItem in items">{{myItem}}</mylist>
</div>
</div>
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);
}
}
}
});
答案 1 :(得分:14)
实现此目的的另一种方法如下。
的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();
});
});
}
};
});
在此similar plunker中查看此操作。基于此long Github issue discussion。
答案 2 :(得分:8)
其他答案很遗憾不适用于最新版本的角度(我检查1.4
)所以我认为分享this 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>
答案 3 :(得分:5)
不需要进行转码,因为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>
请注意,创建新范围是可选的,因此您可以注释掉这一行:
//scope: true,
更新:您可以选择创建隔离范围:
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>
它[指令]必须能够访问子作用域中的item属性...该指令必须能够访问该列表,以便我可以设置正确的监视并更改内容
按照原来的小提琴,我们将使用普通的子范围(即,子范围将原型继承父范围):scope: true,
。这将确保指令可以访问控制器范围内定义的属性,例如items
。
访问生成的DOM项目
指令的link函数有一个element
参数。因此,在上面的HTML中,元素将设置为<ul>
元素。所以我们可以访问所有DOM元素。例如,element.find('li')
或element.children()
。在下面引用的小提琴中,我有$ watch items
数组。 $ watch回调可以访问element
,因此您可以访问生成的DOM项。回调会将element.children()
记录到控制台。
总之,要将自定义行为添加到列表中,只需将指令放到ul或ol上即可。
答案 4 :(得分:0)
我也遇到了同样的问题,最终在ng-transclude
中添加了一些代码,因此它可以监视并接受使用父级的自定义数据。
用法
Grandparent.js
<div>{{ $ctrl.listName }}</div.
<my-list items="$ctrl.movies">
<div>From context: {{ name }}</div>
<div>From grandparent: {{ $ctrl.listName }}</div>
</my-list>
Parent.js(MyList)
<li ng-repeat="item in $ctrl.items track by item.id">
<cr-transclude context="item"></cr-transclude>
</li>
代码更改为ng-transclude
return function ngTranscludePostLink(
...
) {
let context = null;
let childScope = null;
...
$scope.$watch($attrs.context, (newVal, oldVal) => {
context = newVal;
updateScope(childScope, context);
});
...
$transclude(ngTranscludeCloneAttachFn, null, slotName);
...
function ngTranscludeCloneAttachFn(clone, transcludedScope) {
...
$element.append(clone);
childScope = transcludedScope;
updateScope(childScope, context);
...
}
...
function updateScope(scope, varsHash) {
if (!scope || !varsHash) {
return;
}
angular.extend(scope, varsHash);
}
}
完整代码位于Github。
在Codesandbox中可以看到一个有效的示例。