[关闭]
@frank-shaw 2016-09-24T17:56:50.000000Z 字数 2369 阅读 2097

自定义指令中的transclude选项理解

angular.js


参考文章:
1. http://teropa.info/blog/2015/06/09/transclusion.html
2. http://jvandemo.com/the-nitty-gritty-of-compile-and-link-functions-inside-angularjs-directives-part-2-transclusion/
3. https://docs.angularjs.org/guide/directive

初步理解

假设我们定义了一个自定义指令myTest,而且是Element类型的,那么我们就会有基本的用法:<my-test></my-test>,那么如果我想在其中添加自己的内容,像这样:<my-test>我是要显示的内容</my-test>,或者这样:

  1. <my-test>
  2. <span>水杯</span>
  3. </my-test>

这个时候,如何把这其中的内容封装到DOM中呢?这个时候就需要用到transclude选项了。直接上例子(参考官网:https://docs.angularjs.org/guide/directive):

  1. //script.js
  2. (function(angular) {
  3. 'use strict';
  4. angular.module('docsTransclusionExample', [])
  5. .controller('Controller', ['$scope', function($scope) {
  6. //...
  7. }])
  8. .directive('myDialog', function() {
  9. return {
  10. restrict: 'E',
  11. transclude: true,
  12. templateUrl: 'my-dialog.html'
  13. };
  14. });
  15. })(window.angular);
  16. //index.html
  17. <body ng-app="docsTransclusionExample">
  18. <div ng-controller="Controller">
  19. <my-dialog>
  20. <span>Check out the contents</span>
  21. </my-dialog>
  22. </div>
  23. </body>
  24. //my-dialog.html
  25. <div>This is me</div>
  26. <div ng-transclude></div>

上面函数的功能很简单,index.html中自定义指令为myDialog,在script.js中设置该指令的模板URL为my-dialog.html。由于此时的自定义指令中包含内容Check out the contents,所得到的DOM结构会是这样的:

  1. <body ng-app="docsTransclusionExample">
  2. <div ng-controller="Controller">
  3. <div>This is me</div>
  4. <span>Check out the contents</span>
  5. </div>
  6. </body>

这个时候,我们就可以知道transclude的作用了。其中,属性ng-transclude表明的是具体插入的位置。

更多的,实际上transclude选项可选参数有trueelement。我们只分析参数设置为true的情况。

transclude的作用域

作用域一直是一个让人纠结的难点。对上面的例子做一点修改,变成下面的例子,预测一下输出是什么:

  1. //script.js
  2. (function(angular) {
  3. 'use strict';
  4. angular.module('docsTransclusionExample', [])
  5. .controller('Controller', ['$scope', function($scope) {
  6. $scope.name = "Frank";
  7. }])
  8. .directive('myDialog', function() {
  9. return {
  10. restrict: 'E',
  11. transclude: true,
  12. scope:{},
  13. templateUrl: 'my-dialog.html',
  14. link: function(scope) {
  15. scope.name = "shaw";
  16. }
  17. };
  18. });
  19. })(window.angular);
  20. //index.html
  21. <body ng-app="docsTransclusionExample">
  22. <div ng-controller="Controller">
  23. <my-dialog>
  24. <span>Check out the contents, {{name}}</span>
  25. </my-dialog>
  26. </div>
  27. </body>
  28. //my-dialog.html
  29. <div>Hello, {{name}}</div>
  30. <div ng-transclude></div>

它的输出是这样的:
output-transclude.png-2.6kB

这是否和你想象中的一样呢?

我们来分析一下:在自定义指令中,我们已经设置了scope:{},这意味着已经将自定义指令创建的子作用域与父作用域隔离开来了(你应该知道这个)。但是为什么还是会输出Frank呢?而且,同样是my-dialog.html中的文件,<div>Hello, {{name}}</div>的输出是Hello, shaw,而<div ng-transclude></div>输出的却是Check out the contents, Frank???

实际上,transclude选项会创建自己的作用域,而创建的位置就是属性ng-transclude所在的位置及其内部元素。而这个transclude作用域继承的是自定义指令(我们这里是my-dialog)外的作用域。借鉴下图即可理解(忽视左侧的具体HTML内容):
scope-transclude.png-65.5kB

那么这个时候,再回到刚刚的例子中。也就可以理解为什么这么输出了。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注