angularjs中的transclude参数的理解($tranclude服务以及compile中transclude)

首先我们来和replace进行一个对比:



	
		
	
	
		
			
这里是指令内部的内容。
我们看看replace中的代码:

var myModule = angular.module("MyModule", []);
myModule.directive("hello", function() {
    return {
    	restrict:"AE",
    	template:"
Hello everyone!
", replace:true } });
这时候我们很清楚的看到下面的DOM结构:

angularjs中的transclude参数的理解($tranclude服务以及compile中transclude)_第1张图片

也就是说我们的template内容把标签内部的div元素移除了,这有时候不是我们希望看到的结果,因为这种方式完全无法完成标签的嵌套!于是才引入了我们的transclude属性。

我们使用transclude来完成标签的嵌套工作:



	
		
	
	
		
			
这里是指令内部的内容。
我们看看transcude在内部是如何完成的:

var myModule = angular.module("MyModule", []);
myModule.directive("hello", function() {
    return {
    	restrict:"AE",
    	transclude:true,
    	template:"
Hello everyone!
" } });
很显然首先需要把transclude设置为true,然后在template中进行修改, 其中
就是告诉ng把hello标签内部的内容全部放在ng-transclude所在的位置。
DOM结构如下:

angularjs中的transclude参数的理解($tranclude服务以及compile中transclude)_第2张图片

这就比较容易理解了,这里的hello标签没有被替换掉,因为没有指定replace:true,至于内部

还是会原封不动的保存,只是内部原来的DOM结构被这个ng-transclude指定的div包裹起来了!而且内部原来的DOM也被添加了一个内置的class,也就是ng-scope,这一点一定要注意!

我们在最后给出一个综合的例子:


	
		
		
		
		
	
	
	
		
{{text}}
我们看看expander指令的签名:

var expanderModule=angular.module('expanderModule', []);
//expander指令
expanderModule.directive('expander', function() {
	return {
		restrict : 'EA',
		replace : true,
		//直接把expander替换掉了
		transclude : true,
		//transclude表示可以嵌套标签
		scope : {
			title : '=expanderTitle'
		//这里是双向绑定,使用了'='进行双向绑定!
		},
		//直接替换掉,ng-show通过参数'showMe'来决定是否显示。ng-transclude指定了就是expander内部添加到ng-transclude中
		template : '
' + '
{{title}}
' + '
' + '
', //这里指定link函数 link : function(scope, element, attrs) { scope.showMe = false; scope.toggle = function() { scope.showMe = !scope.showMe; } } } }); //这里指定controller控制器 expanderModule.controller('SomeController',function($scope) { //在$scope中指定title和text $scope.title = '点击展开'; $scope.text = '这里是内部的内容。'; });

至此,我们的transclude就可以理解了。

<在控制器中注入的$transclude服务>

           
我们看看内部是如何注入$transclude服务的:

var testapp = angular.module('testapp', []);
testapp.controller('parentController', ['$scope', '$window', function($scope, $window) {
    $scope.onPrimary1Click = function() {
        alert('Primary1 clicked');    
    };
    $scope.primary1Label = "Prime1"
}]);
testapp.directive('primary', function() {
    return {
        restrict: 'C',
        link: function(scope, element, attrs) {
            element.addClass('btn btn-primary');
        }
    }
});
testapp.directive('secondary', function() {
    return {
        restrict: 'C',
        link: function(scope, element, attrs) {
            element.addClass('btn');
        }
    }
});
testapp.directive('buttonBar', function() {
    return {
        restrict: 'EA',
        template: '
', replace: true, transclude: true, scope: {},//这里是独立作用域 //我们在这里注入了$scope,$element,$transclude。这个控制器应该是指令的控制器 controller: ['$scope', '$element', '$transclude', function ($scope, $element, $transclude) { $transclude(function(clone) { //$transclude中接收的函数里的参数含有指令元素的内容(指令元素的内容就是指令内部的元素,也就是应该被transclude的内容,这里指的是[text, button.primary.ng-scope, text, button.primary.ng-scope, text, button.secondary.ng-scope, text]) //$element包含编译后的DOM元素(也就是把指令template进行了编译,这里指的是[div.span4.well.clearfix]),所以就可以在控制器中同时操作DOM元素和指令内容。 var primaryBlock = $element.find('div.primary-block'); var secondaryBlock = $element.find('div.secondary-block'); var transcludedButtons = clone.filter(':button'); angular.forEach(transcludedButtons, function(e) { //每一个对象都有hasClass方法等 if (angular.element(e).hasClass('primary')) { primaryBlock.append(e); } else if (angular.element(e).hasClass('secondary')) { secondaryBlock.append(e); } }); }); }], }; });
最重要的是在我们的自定义指令中指定了一个controller函数:

controller: ['$scope', '$element', '$transclude', function ($scope, $element, $transclude) {
            $transclude(function(clone) {
                //$transclude中接收的函数里的参数含有指令元素的内容(指令元素的内容就是指令内部的元素,也就是应该被transclude的内容,这里指的是[text, button.primary.ng-scope, text, button.primary.ng-scope, text, button.secondary.ng-scope, text])
                //$element包含编译后的DOM元素(也就是把指令template进行了编译,这里指的是[div.span4.well.clearfix]),所以就可以在控制器中同时操作DOM元素和指令内容。
                var primaryBlock = $element.find('div.primary-block');
                var secondaryBlock = $element.find('div.secondary-block');
              var transcludedButtons = clone.filter(':button'); 
                angular.forEach(transcludedButtons, function(e) {
                    //每一个对象都有hasClass方法等
                    if (angular.element(e).hasClass('primary')) {
                        primaryBlock.append(e);
                    } else if (angular.element(e).hasClass('secondary')) {
                        secondaryBlock.append(e);
                    }
                });
            });
        }],
    };
这时候我们在$transclude中传入的函数接受一个参数,这个参数代表我们需要通过transclude包含的内容,说白了也就是 我们的自定义指定内部的子元素集合!而$element就是我们指令的template编译后得到的DOM结构:我们看看这个接受到的clone的签名是怎么样的:

angularjs中的transclude参数的理解($tranclude服务以及compile中transclude)_第3张图片

很显然就是我们在页面中自定义指令内部的元素进行编译后的DOM元素,有一点注意:这个编译后的DOM含有空格,也就是text元素类型。但是我们可能只是需要其中的button元素,于是在上面引入了filter方法,但是一直报错说"angular.js:62 TypeError: clone.filter is not a function",但是这个例子确实是可以运行的,所以这里我记录下来,以后慢慢理解。我们看看这时候ng为我们提供了那些方法,这些方法都是来源于jqLite的:

angularjs中的transclude参数的理解($tranclude服务以及compile中transclude)_第4张图片

angularjs中的transclude参数的理解($tranclude服务以及compile中transclude)_第5张图片
很显然如果我们需要对DOM进行操作,这些方法都是可以调用的。

<编译函数参数中的transclude>:

DOM结构和上面一样,我们看看指令内部的代码:

var testapp = angular.module('testapp', []);
//这里注入了$window对象
testapp.controller('parentController', ['$scope', '$window', function($scope, $window) {
    $scope.primary1Label = 'Prime1';
    $scope.onPrimary1Click = function() {
        $window.alert('Primary 1 clicked');                
    }
}]);
testapp.directive('primary', function() {
    return {
        restrict: 'C',
        link: function(scope, element, attrs) {
            element.addClass('btn btn-primary');
        }
    }
});
testapp.directive('secondary', function() {
    return {
        restrict: 'C',
        link: function(scope, element, attrs) {
            element.addClass('btn');
        }
    }
});
//这里是我们的指令buttonBar
testapp.directive('buttonBar', function() {
    return {
        restrict: 'EA',
        template: '
', replace: true, transclude: true, //为我们的编译函数参数中的transclude compile: function(elem, attrs, transcludeFn) { return function (scope, element, attrs) { //这里我们指定了第一个参数为scope transcludeFn(scope, function(clone) { var primaryBlock = elem.find('div.primary-block'); var secondaryBlock = elem.find('div.secondary-block'); //这里的clone就是我们的应该被transclude的内容的DOM结构,当然其中也是包含空格符号的,所以需要过滤 console.log(clone); var transcludedButtons = clone.filter(':button'); //对我们的button元素进行遍历 angular.forEach(transcludedButtons, function(e) { if (angular.element(e).hasClass('primary')) { primaryBlock.append(e); } else if (angular.element(e).hasClass('secondary')) { secondaryBlock.append(e); } }); }); }; } }; });
其实这种方式是在我们自定义指令中使用了一个compile函数,并且在编译函数compile中指定了transclude参数:

 compile: function(elem, attrs, transcludeFn) {
            return function (scope, element, attrs) {
                //这里我们指定了第一个参数为scope
                transcludeFn(scope, function(clone) {
                    var primaryBlock = elem.find('div.primary-block');
                    var secondaryBlock = elem.find('div.secondary-block');
                    //这里的clone就是我们的应该被transclude的内容的DOM结构,当然其中也是包含空格符号的,所以需要过滤
                    var transcludedButtons = clone.filter(':button'); 
                    //对我们的button元素进行遍历
                    angular.forEach(transcludedButtons, function(e) {
                        if (angular.element(e).hasClass('primary')) {
                            primaryBlock.append(e);
                        } else if (angular.element(e).hasClass('secondary')) {
                            secondaryBlock.append(e);
                        }
                    });
                });
            };
        }
我们为transclude函数传入了第一个参数为scope,同时第二个参数为一个函数,这个函数接受来自于自定义指令的内部元素,也就是需要被添加到ng-transclude指令中的元素。而外层函数compile接受第一个参数为elem,其代表的对template进行编译后的DOM树,第三个参数才是我们的transclude函数。阅读 angular中的transclude一文定能收获不少

你可能感兴趣的:(angularJS)