AngularJS:如何覆盖Transclude作用域?指令初始化问题咨询
嘿,这个问题我之前也碰到过!核心原因就是AngularJS默认的transclude机制会把被嵌入的内容(就是你<dir>标签里的<h3>们)绑定到指令所在的父作用域,而不是指令的隔离作用域,更不是ng-repeat循环里的每个子作用域。所以你直接写{{obj.name}}的时候,它在父作用域里找obj,自然找不到,只能通过$parent去跳转到循环的子作用域里取值。
要实现直接用{{obj.name}}访问,我们需要手动控制transclude的作用域绑定,把嵌入内容的作用域和ng-repeat每个循环项的作用域关联起来。下面给你两种可行的方案:
方案一:手动创建循环项作用域(推荐)
这种方法不依赖ng-repeat的内部实现,更可靠:
angular.module("myApp", []).directive('dir', function() { return { restrict: 'E', transclude: true, scope: { arr: '=' }, template: '<div class="dir-container"></div>', link: function(scope, element, attrs, ctrl, transclude) { // 监听数组变化,实时更新内容 scope.$watch('arr', function(newItems) { if (!newItems) return; const container = element.find('.dir-container'); container.empty(); newItems.forEach(item => { // 创建一个继承自指令隔离作用域的子作用域,挂载当前循环项 const itemScope = scope.$new(); itemScope.obj = item; // 将transclude内容绑定到这个子作用域,然后插入到容器中 transclude(itemScope, function(clone) { container.append(clone); }); }); }, true); // 深度监听数组内部元素的变化 } }; });
修改后你的HTML不需要任何变化,直接用{{obj.name}}就能正常访问循环项的name属性了。
方案二:绑定到ng-repeat的子作用域
如果你想保留ng-repeat在模板里,可以通过获取ng-repeat生成的子作用域来绑定transclude内容:
angular.module("myApp", []).directive('dir', function() { return { restrict: 'E', transclude: true, scope: { arr: '=' }, template: '<span ng-repeat="obj in arr" class="repeat-item"></span>', link: function(scope, element, attrs, ctrl, transclude) { scope.$watch('arr', function() { // 等待ng-repeat完成渲染 setTimeout(() => { const repeatItems = element.find('.repeat-item'); let currentScope = scope.$$childHead; // 获取第一个循环项的子作用域 repeatItems.each(function() { if (!currentScope) return; // 将transclude内容绑定到当前循环项的作用域 transclude(currentScope, function(clone) { angular.element(this).append(clone); }.bind(this)); currentScope = currentScope.$$nextSibling; // 切换到下一个循环项的作用域 }); }, 0); }, true); } }; });
这个方案依赖AngularJS内部的作用域属性($$childHead、$$nextSibling),虽然能工作,但不如方案一稳定,不推荐在生产环境大量使用。
原理说明
默认的transclude: true会创建一个继承自指令父作用域的作用域来渲染嵌入内容,和指令的隔离作用域、ng-repeat的子作用域是相互独立的。我们通过手动调用transclude(scope, cloneFn)方法,把嵌入内容的作用域指定为循环项的子作用域,这样嵌入内容里的表达式就能直接访问该作用域上的obj变量了。
内容的提问来源于stack exchange,提问作者Honchar Denys

