AngularJS Custom-Directives transclude, ngTransclude guide

AngularJS Directive’s transclude & ng-transclude can be used to create directive that wraps other elements. By-default, AngularJS Directive element replaces any element declared as a child element of directive in HTML, by directive’s own content. But sometimes you way want to preserve the child element and let the directive act as more of a wrapper rather than replacement. This can be done thanks to transclude & ng-transclude.

This post is a part of AngularJS Directives Tutorial Series.

Let’s understand it with help of few examples. In all our previous examples, you have noticed that there is no child-element added under a directive element. Now if you try to wrap an element by a directive, something like shown below:

			<div ng-repeat="bla in ctrl.items">
				<item-widget item="bla" promo="Christmas-Sale" on-select="ctrl.onItemSelect(selectedItem)">
					Last item left. Hurry up. 
				</item-widget>
			</div>

on execution, you will see that child element of this widget [that poor dummy message] is found nowhere. It gets kicked out by directive’s own content [template]. There might however be cases when we want to preserve the child element of a directive, OR in other words, wrap the existing content by the directive’s own content.

AngularJS provides transclude option and ng-transclude directive to help with this issue. It’s a two step process:

  • 1. Including transclude:true in directive definition.
  • 2. Include ng-transclude directive as an element in directive’s own content. ng-transclude acts like a placeholder.

How it works?

  • Whenever AngularJS encounters a directive in HTML, and if that directive definition has transclude set to true, AngularJS will make a copy of content the directive is applied on, and will store it somewhere so that it does not get lost when angular replaces it with directive’s own template.
  • AngularJS needs to know where to put the content it copied in previous step. Since we want to make that content as a child of directive, why not create a placeholder in directive’s content itself, which will then be replaced by content copied above. That’s exactly angularJS does. It provides ng-transclude directive that can be used as a placeholder somewhere in directive’s own content, that then be replaced by element’s content stored in previous step.

Live Example:

Let’s get into details:

			<div ng-repeat="bla in ctrl.items">
				<item-widget item="bla" promo="Christmas-Sale" on-select="ctrl.onItemSelect(selectedItem)">
				    Last item left. Hurry up. 
				</item-widget>
			</div>


			.directive('itemWidget', [function() {
				return{
					transclude:true,
					...//other options as in previous examples.
				}
			}]);

Firstly, We have included dummy content as child of our custom widget, and our intention would be to preserve it. Next, we have declarted transclude key with value true.

Now let’s see the directive’s template [saleItem6.html]

<div class="panel panel-default">
	<div class="panel-heading">
		Published at:<span ng-bind="item.published | date"></span> &nbsp;Promotion: {{promo}}<button class="pull-right" ng-click="pickMe({selectedItem:item.name})">Buy me</button>
	</div>
	<div class="panel-body">
			<div class="alert alert-info" ng-transclude></div>
			Name:<span ng-bind="item.name"></span>
			Condition:<span ng-bind="item.condition"></span>
			Price:<span ng-bind="item.price | currency"></span>
			Brand:<span ng-bind="item.brand"></span>
	</div>
</div>

In above directive’s template file, we have included ng-transclude with a div. AngularJS will copy the content of the element it finds when it encounters our directive in HTML, and make it as a child element of this div element with ng-transclude.

Complete code

<html>
	<head>  
		<title>Directive Demo</title>  
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
	</head>
	<body class="jumbotron container" ng-app="myApp">
		<div ng-controller="AppController as ctrl">
			<h3>List of Sale Items.</h3>
			<div ng-repeat="bla in ctrl.items">
				<item-widget item="bla" promo="Christmas-Sale" on-select="ctrl.onItemSelect(selectedItem)">
				    Last item left. Hurry up. 
				</item-widget>
			</div>
		</div>
      
		<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js">
		</script>  
		<script>
			angular.module('myApp', [])
			.controller('AppController', [function() {
				var self = this;
				self.items = [
							{name: 'Computer', price: 500, condition:'New',brand : 'Lenovo', published:'01/11/2015'},
							{name: 'Phone', price: 200, condition:'New',brand : 'Samsung', published:'02/11/2015'},
							{name: 'Printer', price: 300, condition:'New',brand : 'Brother', published:'06/11/2015'},
							{name: 'Dishwasher', price: 250, condition:'Second-Hand',brand : 'WhirlPool', published:'01/12/2015'},
							];
				self.onItemSelect = function(name) {
					console.log('Congrats. You have just bought a', name);
				};							
			}])
			.directive('itemWidget', [function() {
				return{
					restrict: 'E',
					replace:true,
					transclude:true,
					scope: {
						item: '=',
						promo: '@',
						pickMe : '&onSelect'
					},
    				templateUrl: 'saleItem6.html',
					link : function(scope, element, attrs){
					    //Add event listener for 'click' event					
						element.on('click', function(event) {
						
								element.html('Thanks for buying this item.');
								element.css({
									color: 'green'
								});
						  });
					}
				}
			}]);
		</script>
	</body>
</html>

That’s it for transclude option with ngTransclude. Let’s move to advanced link-function configuration option [require] of Directive Definition Object where we discuss inter-directive communication in detail using directive’s controllers.

Next Topic : AngularJS Directives controllers & require option


Please note that if your AngularJS application is using templateUrl to access HTML partials, you will need an HTTP server on front to serve them. This is due to the fact that Browser’s does not allow serving or requesting files on the file:// protocol.

You can use popular Apache HTTP server to serve files. If you need assistance setting-up server,
this post can help you.

Download Source Code

References