AngularJS中transclude用法詳解
本文實(shí)例講述了AngularJS中transclude用法。分享給大家供大家參考,具體如下:
Transclude - 在Angular的指令中,大家會(huì)看到有一個(gè)這樣的一個(gè)配置屬性,這個(gè)單詞在英文字典里面也查詢不到真實(shí)的意思,所以就用英文來(lái)標(biāo)示它吧。如果你深入的使用angular的話,你就花很大一部分時(shí)間來(lái)創(chuàng)建自定義指令,那么就不可避免的要深入理解transclude。簡(jiǎn)單的講,transclude主要完成以下工作,取出自定義指令中的內(nèi)容(就是寫(xiě)在指令里面的子元素),以正確的作用域解析它,然后再放回指令模板中標(biāo)記的位置(通常是ng-transclude標(biāo)記的地方),雖然使用內(nèi)建的ngTransclude對(duì)于基本的transclude操作已經(jīng)足夠簡(jiǎn)單,但是在文檔中對(duì)這個(gè)transclude的解釋還是有存在很多疑惑,比如說(shuō):
在compile函數(shù)中接收到了一個(gè)叫transclude的參數(shù)是什么東西呢?有什么用呢?
在控制器中也有個(gè)叫$transclude的可以通過(guò)依賴注入的服務(wù),這又是什么呢?
隔離作用域跟transclude有什么關(guān)系?
屬性的transclude操作
接下來(lái)我們將一個(gè)個(gè)的解釋:
基本的transclude
我們通過(guò)一個(gè)基本的transclude例子來(lái)講解吧,我們現(xiàn)在要?jiǎng)?chuàng)建的是一個(gè)叫buttonBar的指令,用戶可以通過(guò)它來(lái)添加一組button到頁(yè)面上,這個(gè)指令會(huì)對(duì)不同的button進(jìn)行位置的排列。以下例子css樣式是使用Bootstrap框架。
在fiddle中查看例子:http://jsfiddle.net/ospatil/A969Z/157/
<div ng-controller="parentController">
<button-bar>
<button class="primary" ng-click="onPrimary1Click()">{{primary1Label}}</button>
<button class="primary">Primary2</button>
</button-bar>
</div>
JS:
var testapp = angular.module('testapp', []);
testapp.controller('parentController', ['$scope', '$window', function($scope, $window) {
console.log('parentController scope id = ', $scope.$id);
$scope.primary1Label = 'Prime1';
$scope.onPrimary1Click = function() {
$window.alert('Primary1 clicked');
};
}]);
testapp.directive('primary', function() {
return {
restrict: 'C',
link: function(scope, element, attrs) {
element.addClass('btn btn-primary');
}
}
});
testapp.directive('buttonBar', function() {
return {
restrict: 'EA',
template: '<div class="span4 well clearfix"><div class="pull-right" ng-transclude></div></div>',
replace: true,
transclude: true
};
});
我們先看下HTML標(biāo)簽,buttonBar指令包裹著幾個(gè)button元素。而button元素也被鏈接上了基于class的primary指令,不要太在意這個(gè)primary指令的功能它只不過(guò)為button元素添加一些css的樣式而已。現(xiàn)在我們來(lái)看buttonBar指令,它提供了一個(gè)transclude:true屬性,同時(shí)在它的模板里面使用ng-transclude指令。在運(yùn)行的過(guò)程中,Angular獲取到自定義指令的內(nèi)容,處理完了之后把結(jié)果放到了模板中鏈接上ng-transclude的div。
transclude到多個(gè)位置
現(xiàn)在我們來(lái)增強(qiáng)下我們的buttonBar指令的功能,我們?cè)黾恿藘煞N按鈕,primary和secondary,其中primary按鈕是排右邊,secondary是排左邊。所以要做到這個(gè)功能,它必須能夠取出指令的內(nèi)容,然后把它們分別添加到不同的div中,一個(gè)用來(lái)放primary按鈕, 一個(gè)用來(lái)放secondary按鈕。
這樣的話,默認(rèn)的機(jī)制已經(jīng)滿足不了我們的要求,于是我們有了另外一種方法:
設(shè)置transclude為true
手工移動(dòng)button元素到合適的div
最后,在指令的編譯或鏈接函數(shù)中移除原始的用來(lái)transclude操作的元素
這種方法就是先把所有的內(nèi)容插入到ng-transclude標(biāo)記的元素中,然后在link函數(shù)中再找出元素的插入的元素,重新放到元素的其他地方,最后刪除原來(lái)暫存內(nèi)容的元素。
在fiddle中查看例子:http://jsfiddle.net/ospatil/A969Z/158/
<div ng-controller="parentController">
<button-bar>
<button class="primary" ng-click="onPrimary1Click()">{{primary1Label}}</button>
<button class="primary">Primary2</button>
<button class="secondary">Secondary1</button>
</button-bar>
</div>
JS:
var testapp = angular.module('testapp', []);
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');
}
}
});
testapp.directive('buttonBar', function() {
return {
restrict: 'EA',
template: '<div class="span4 well clearfix"><div class="primary-block pull-right"></div><div class="secondary-block"></div><div class="transcluded" ng-transclude></div></div>',
replace: true,
transclude: true,
link: function(scope, element, attrs) {
var primaryBlock = element.find('div.primary-block');
var secondaryBlock = element.find('div.secondary-block');
var transcludedBlock = element.find('div.transcluded');
var transcludedButtons = transcludedBlock.children().filter(':button');
angular.forEach(transcludedButtons, function(elem) {
if (angular.element(elem).hasClass('primary')) {
primaryBlock.append(elem);
} else if (angular.element(elem).hasClass('secondary')) {
secondaryBlock.append(elem);
}
});
transcludedBlock.remove();
}
};
});
雖然這種方法達(dá)到了我們的目的,但是允許默認(rèn)的transclude操作,然后再人工的從DOM元素中移出不是非常有效率的。因此,我們有了compile函數(shù)中的transclude參數(shù)和控制器中的$transclude服務(wù)
編譯函數(shù)參數(shù)中的transclude
開(kāi)發(fā)者指南中給了我們以下的關(guān)于指令中編譯函數(shù)的形式:
function compile(tElement, tAttrs, transclude) { ... }
其中關(guān)于第三個(gè)參數(shù)transclude的解釋是:
transclude - A transclude linking function: function(scope, cloneLinkingFn).
好的,現(xiàn)在我們利用這個(gè)函數(shù)來(lái)實(shí)現(xiàn)我們剛才講到的功能,從而不需要再先暫存內(nèi)容,然后再插入到其他地方。
在fiddle中查看例子:http://jsfiddle.net/ospatil/A969Z/161/
<div ng-controller="parentController">
<button-bar>
<button class="primary" ng-click="onPrimary1Click()">{{primary1Label}}</button>
<button class="primary">Primary2</button>
<button class="secondary">Secondary1</button>
</button-bar>
</div>
JS:
var testapp = angular.module('testapp', []);
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');
}
}
});
testapp.directive('buttonBar', function() {
return {
restrict: 'EA',
template: '<div class="span4 well clearfix"><div class="primary-block pull-right"></div><div class="secondary-block"></div></div>',
replace: true,
transclude: true,
compile: function(elem, attrs, transcludeFn) {
return function (scope, element, attrs) {
transcludeFn(scope, function(clone) {
var primaryBlock = elem.find('div.primary-block');
var secondaryBlock = elem.find('div.secondary-block');
var transcludedButtons = clone.filter(':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);
}
});
});
};
}
};
});
注意到,transcludeFn函數(shù)需要一個(gè)可用的scope作為第一個(gè)參數(shù),但是編譯函數(shù)中沒(méi)有可用的scope,所以這里需要在鏈接函數(shù)中執(zhí)行transcludeFn。這種方法實(shí)際上是在link函數(shù)中同時(shí)操作編譯后的DOM元素和模板元素(主要是因?yàn)閠ranscludeFn函數(shù)中保存著指令的內(nèi)容)。
可在控制器中注入的$transclude服務(wù)
在開(kāi)發(fā)者指南中對(duì)$transclude服務(wù)是這么解釋的:
$transclude - A transclude linking function pre-bound to the correct transclusion scope: function(cloneLinkingFn).
看看如何用在我們的例子中:
在fiddle中查看例子:http://jsfiddle.net/ospatil/A969Z/162/
<div ng-controller="parentController">
<button-bar>
<button class="primary" ng-click="onPrimary1Click()">{{primary1Label}}</button>
<button class="primary">Primary2</button>
<button class="secondary">Secondary1</button>
</button-bar>
</div>
JS:
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: '<div class="span4 well clearfix"><div class="primary-block pull-right"></div><div class="secondary-block"></div></div>',
replace: true,
transclude: true,
scope: {},
controller: ['$scope', '$element', '$transclude', function ($scope, $element, $transclude) {
$transclude(function(clone) {
var primaryBlock = $element.find('div.primary-block');
var secondaryBlock = $element.find('div.secondary-block');
var transcludedButtons = clone.filter(':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中接收的函數(shù)里的參數(shù)含有指令元素的內(nèi)容,而$element包含編譯后的DOM元素,所以就可以在控制器中同時(shí)操作DOM元素和指令內(nèi)容,跟上文的compile函數(shù)的實(shí)現(xiàn)方式有異曲同工之處,這里有幾點(diǎn)需要注意,這個(gè)控制器應(yīng)該是指令的控制器,另一個(gè)注意到上文除了第一種方法,其他的地方都沒(méi)有用到ng-transclude,因?yàn)闊o(wú)需插入到模板中。
Transclude 和 scope
在開(kāi)發(fā)者指南中提到了a directive isolated scope and transclude scope are siblings,這到底是什么意思呢?假如你認(rèn)真看前文的例子的話,你就會(huì)發(fā)現(xiàn)parentController控制器創(chuàng)建了一個(gè)作用域,buttonBar指令在parentController下面創(chuàng)建了一個(gè)孤立作用域,而根據(jù)Angular文檔,transclude也創(chuàng)建了另外一個(gè)作用域,因此指令的隔離作用域跟transclude作用域是基于同一個(gè)父作用域的兄弟作用域。
transclude內(nèi)容放入元素的屬性
實(shí)際上,你不可以這么做,但是你可以通過(guò)一種變通的方法來(lái)實(shí)現(xiàn)這種效果
var testapp = angular.module('testapp', [])
testapp.directive('tag', function() {
return {
restrict: 'E',
template: '<h1><a href="{{transcluded_content}}">{{transcluded_content}}</a></h1>',
replace: true,
transclude: true,
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function(scope) {
transclude(scope, function(clone) {
scope.transcluded_content = clone[0].textContent;
});
}
}
}
}
});
這里沒(méi)有操作DOM元素,只是把元素的文本內(nèi)容復(fù)制給了作用域?qū)傩?,然后在通過(guò)作用域傳給屬性。
另外要注意的是,這里的clone參數(shù)是jquery或angular.element封裝的整個(gè)模板元素。
// todo add comparing with ng-include
希望本文所述對(duì)大家AngularJS程序設(shè)計(jì)有所幫助。
相關(guān)文章
AngularJS日期格式化常見(jiàn)操作實(shí)例分析
這篇文章主要介紹了AngularJS日期格式化常見(jiàn)操作,結(jié)合實(shí)例形式分析了AngularJS日期格式化常用參數(shù)功能、設(shè)置與使用技巧,需要的朋友可以參考下2018-05-05
angular.js+node.js實(shí)現(xiàn)下載圖片處理詳解
這篇文章主要介紹了angular.js+node.js實(shí)現(xiàn)下載圖片處理的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-03-03
Commands Queries設(shè)計(jì)模式提高Angular應(yīng)用性能及可維護(hù)性
在Angular應(yīng)用開(kāi)發(fā)領(lǐng)域,Commands and Queries 設(shè)計(jì)模式是一個(gè)關(guān)鍵的概念,它有助于有效地管理應(yīng)用程序的狀態(tài)和與后端的交互,本文將深入探討這一設(shè)計(jì)模式的核心要點(diǎn),并通過(guò)實(shí)際示例來(lái)加以說(shuō)明2023-10-10
使用Angular CLI生成 Angular 5項(xiàng)目教程詳解
這篇文章主要介紹了使用Angular CLI生成 Angular 5項(xiàng)目的教程詳解 ,需要的朋友可以參考下2018-03-03
AngularJS實(shí)現(xiàn)一次監(jiān)聽(tīng)多個(gè)值發(fā)生的變化
這文章給大家介紹了如何利用AngularJS一次監(jiān)聽(tīng)多個(gè)值發(fā)生的變化,文中通過(guò)示例代碼演示,這樣更方便大家理解學(xué)習(xí),有需要的可以參考借鑒。2016-08-08
將Angular單項(xiàng)目升級(jí)為多項(xiàng)目的全過(guò)程
有時(shí)候在開(kāi)發(fā)的過(guò)程中發(fā)現(xiàn)一個(gè)Angular項(xiàng)目不太夠用,兩個(gè)獨(dú)立的項(xiàng)目又不太好復(fù)用,這時(shí)便需要將原來(lái)的Angular項(xiàng)目簡(jiǎn)單做個(gè)升級(jí),這篇文章主要給大家介紹了關(guān)于將Angular單項(xiàng)目升級(jí)為多項(xiàng)目的相關(guān)資料,需要的朋友可以參考下2021-11-11
Angular.js中angular-ui-router的簡(jiǎn)單實(shí)踐
本篇文章主要介紹了Angular.js中angular-ui-router的簡(jiǎn)單實(shí)踐,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07

