自定义子指令访问父指令的作用域
自定义子指令访问父指令的作用域
我在我的AngularJS应用中有两个自定义指令。一个充当父指令,另一个充当子指令。我想在子指令中访问父指令的作用域。但是我没有得到期望的输出。\n
{{myName}}
\n我的脚本如下:\n
var app = angular.module("sampleApp",[]); app.controller("CountryCtrl",function($scope){ $scope.myName = "India"; }); app.controller("StateCtrl",function($scope){ }); app.directive("state",function(){ return { restrict : 'E', transclude: true, scope : { myName : '=nameofthestate'}, template:"** {{myName}} is inside {{$parent.myName}}" } }); app.directive("city",function(){ return { restrict : 'E', require:'^state', scope : { myName : '=nameofthecity'}, template:"**** {{myName}} is inside {{$parent.myName}} which is in {{$parent.$parent.myName }} " } });
\n对应的JSFiddle在https://jsbin.com/nozuri/edit?html,js,output中可用。\n我得到的输出是:\n
India ** Tamilnadu is inside India **** Chennai is inside India which is in Tamilnadu
\n期望的输出是:\n
India ** Tamilnadu is inside India **** Chennai is inside Tamilnadu which is in India
\n有人能告诉我在这里我做错了什么吗?
当AngularJS遇到transclude指令时,它会在用template或templateUrl内容替换之前克隆HTML。然后,当遇到ng-transclude指令时,它会编译被传递的内容,但是将它链接到父作用域而不是指令的隔离作用域。因此,被传递的内容仍然可以访问父控制器及其内容,而指令的HTML具有隔离作用域(或者可能是一个新的作用域)。
这个问题的出现是因为当使用transclude指令时,被传递内容的作用域会被链接到父作用域,而不是指令的隔离作用域。这可能会导致在指令内部无法访问父作用域的数据或方法,限制了指令的灵活性和可复用性。
解决这个问题的方法是使用$compile服务手动链接被传递内容的作用域到指令的隔离作用域。通过在指令的链接函数中获取transcludeFn,并将其链接到指令的作用域,可以实现这一点。
以下是一个示例代码,演示了如何解决这个问题:
angular.module('myApp').directive('myDirective', function($compile) { return { restrict: 'E', transclude: true, scope: {}, template: '', link: function(scope, element, attrs, ctrl, transcludeFn) { transcludeFn(scope, function(clone) { element.append(clone); }); } }; });Directive Content:
在这个例子中,我们创建了一个名为myDirective的自定义指令。在指令的链接函数中,我们使用$compile服务手动链接被传递内容的作用域到指令的隔离作用域。通过调用transcludeFn并将其链接到指令的作用域,我们可以确保被传递的内容可以访问指令的作用域。
通过使用$compile服务手动链接被传递内容的作用域,我们可以解决指令内部无法访问父作用域的问题,从而增加指令的灵活性和可复用性。
在上述代码中,我们可以看到两个自定义的指令:state和city。state指令内部有一个controller函数,用来获取父级作用域的变量myName。city指令通过require属性将state指令作为依赖,并在link函数中获取state指令内部controller的方法getName()返回的值,并将其赋值给自己的作用域变量parentName。
这种情况下的问题是,当父级作用域的变量myName是一个可能会发生变化的数组时,在视图中不会实时更新。为了解决这个问题,可以使用$scope.$watch()来监听这个变量,当它发生变化时更新视图。
以下是修改后的代码:
var app = angular.module('myApp', []); app.controller("CountryCtrl",function($scope){ $scope.myName = "India"; }); app.controller("StateCtrl",function($scope){ }); app.directive("state",function(){return { restrict : 'E', transclude: true, scope : { myName : '=nameofthestate'}, template:"** {{myName}} is inside {{$parent.myName}}", controller: function ($scope) { this.getName = function () { return $scope.myName; } } }}); app.directive("city",function(){return { restrict : 'E', require:'^state', scope : { myName : '=nameofthecity'}, template:"**** {{myName}} is inside {{parentName}} which is in {{$parent.myName }} ", link: function(scope, element, attrs, ctrl) { scope.parentName = ctrl.getName(); // 监听父级作用域的变量myName scope.$watch(function() { return scope.$parent.myName; }, function(newVal) { scope.parentName = newVal; }); } }});
在city指令的link函数中添加了一个$scope.$watch(),监听父级作用域的变量myName,并在变化时更新city指令的作用域变量parentName。
这样,当父级作用域的变量myName发生变化时,city指令的视图也会实时更新。
在AngularJS中,当自定义指令嵌套时,有时候子指令需要访问父指令的作用域。但是在一些情况下,子指令无法直接访问父指令的作用域,这就是问题“Custom child directive accessing scope of parent”出现的原因。
在给定的内容中,问题的原因是父指令的作用域是被传递的,它继承自控制器的作用域。这就是为什么$parent.MyName = India。此外,由于state指令的作用域是隔离的(scope = {} ),所以传递作用域的$parent是state指令的作用域。这就是为什么$parent.$parent.MyName = Tamilnadu。这是Angular 1.3更新的一部分。
解决这个问题的方法是使用transclude属性。当指令使用transclude: true时,它会创建一个新的“传递”子作用域,该作用域从父作用域继承。如果指令还创建了一个隔离作用域,传递作用域和隔离作用域将成为兄弟作用域。每个作用域的$parent属性引用同一个父作用域。
在Angular 1.3更新中,如果指令还创建了一个隔离作用域,传递作用域现在成为隔离作用域的子作用域。传递作用域和隔离作用域不再是兄弟作用域。传递作用域的$parent属性现在引用隔离作用域。
此外,Matthew的回答也提到了父子指令之间的通信问题。