Using template from 1 directive into another directives template - angularjs

Basically I have 2 custom directives, each having it's own template. What I need is to insert one of the templates into the other. I have also read about transclusion, but can't wrap my head around it. Any ideas would be of great help!

From the AngularJS Website, an example:
<script>
angular.module('transcludeExample', [])
.directive('pane', function(){
return {
restrict: 'E',
transclude: true,
scope: { title:'#' },
template: '<div style="border: 1px solid black;">' +
'<div style="background-color: gray">{{title}}</div>' +
'<ng-transclude></ng-transclude>' +
'</div>'
};
})
.controller('ExampleController', ['$scope', function($scope) {
$scope.title = 'Lorem Ipsum';
$scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
}]);
</script>
<div ng-controller="ExampleController">
<input ng-model="title" aria-label="title"> <br/>
<textarea ng-model="text" aria-label="text"></textarea> <br/>
<pane title="{{title}}"><span>{{text}}</span></pane>
</div>
You have to create the custom directive, in this case "pane", from inside angular.module. When you have done that, the directive exists from within the module which should be your application and you can use it freely as it is returned from the directive example. In this case the example uses the "pane" directive and it associates the transcluded template to it.

Related

Can attribute directives transclude?

I want slotted transclusion, and I've seen examples of element directives like this:
<my-directive>
<slot-a></slot-a>
<slot-b></slot-b>
</my-directive>
I want to know if it must be an element directive. I'd like to do something like this:
<div my-directive>
<slot-a></slot-a>
<slot-b></slot-b>
</div>
Is this possible? I can't find any documentation saying it can or can't be done.
Apparently you can—at least in recent versions of AngularJS. The snippet below is a variation of the element directive in the multi-slot transclusion section.
(function(angular) {
'use strict';
angular.module('multiSlotTranscludeExample', [])
.directive('pane', function() {
return {
restrict: 'A',
transclude: {
'title': '?paneTitle',
'body': 'paneBody',
'footer': '?paneFooter'
},
template: '<div style="border: 1px solid black;">' +
'<div class="title" ng-transclude="title">Fallback Title</div>' +
'<div ng-transclude="body"></div>' +
'<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
'</div>'
};
})
.controller('ExampleController', ['$scope',
function($scope) {
$scope.title = 'Lorem Ipsum';
$scope.link = 'https://google.com';
$scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
}
]);
})(window.angular);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<body ng-app="multiSlotTranscludeExample">
<style>
.title,
.footer {
background-color: gray
}
</style>
<div ng-controller="ExampleController">
<input ng-model="title" aria-label="title">
<br/>
<textarea ng-model="text" aria-label="text"></textarea>
<br/>
<div pane>
<pane-title>
<a ng-href="{{link}}" ng-bind="title"></a>
</pane-title>
<pane-body>
<p ng-bind="text"></p>
</pane-body>
</div>
</div>
</body>

Understanding the contextual usage of scope in angular directive

I was just going through the documentation of Angular.js for ngTransclude, i came across the following example:
<script>
angular.module('transcludeExample', [])
.directive('pane', function(){
return {
restrict: 'E',
transclude: true,
scope: { title:'#' },
template: '<div style="border: 1px solid black;">' +
'<div style="background-color: gray">{{title}}</div>' +
'<ng-transclude></ng-transclude>' +
'</div>'
};
})
.controller('ExampleController', ['$scope', function($scope) {
$scope.title = 'Lorem Ipsum';
$scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
}]);
</script>
<div ng-controller="ExampleController">
<input ng-model="title" aria-label="title"> <br/>
<textarea ng-model="text" aria-label="text"></textarea> <br/>
<pane title="{{title}}">{{text}}</pane>
</div>
I am not quite sure why these two properties are used in the directive:
transclude: true,
scope: { title:'#' },
I beleive doing transclude: true, gives the directive access to
$scope.title
$scope.text
which is in the controller , i am not sure about this though , but why is scope being used here ? i mean in such a weird fashion that too, I.E. ,
scope: { title:'#' },
What is the # there for ? so to sum up my whole question, can somebody explain to me why the transclude and scope properties are used here in the directive ?
Thank you
The transclude property tells angular to replace the <ng-transclude> tag with the HTML code inside the directive. In your case, the {{text}} string.
The property:
scope: { title:'#' },
tells angular to include the attribute title passed to the directive in its scope.
More documentation here:
What is the difference between '#' and '=' in directive scope in AngularJS?
and of course here:
https://docs.angularjs.org/guide/directive

Ng-transclude with nested directives

I am working with 2 directives where one directives template contains the second directive. I would like to use ng-transclude inside the second (nested) directive. How can I accomplish this? Here is a plnkr.
<body ng-app="transcludeExample">
<script>
angular.module('transcludeExample', [])
.directive('pane', function(){
return {
restrict: 'E',
transclude: true,
scope: { title:'#' },
template: '<div style="border: 1px solid black;">' +
'<div style="background-color: yellow">{{title}}</div>' +
'<test></test>' +
//'<ng-transclude></ng-transclude>' +
'</div>'
};
})
.directive('test', function(){
return {
restrict: 'E',
transclude: true,
template: '<div><ng-transclude></ng-transclude></div>'
};
})
.controller('ExampleController', ['$scope', function($scope) {
$scope.title = 'Lorem Ipsuma';
$scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
}]);
</script>
<div ng-controller="ExampleController">
<input ng-model="title"> <br/>
<textarea ng-model="text"></textarea> <br/><br/><br/><br/><br/><br/>
<pane title="{{title}}">{{text}}</pane>
</div>
</body>
Don't know if I'm missing something, but this seems to work:
template: '<div style="border: 1px solid black;">' +
'<div style="background-color: yellow">{{title}}</div>' +
'<test><ng-transclude></ng-transclude></test>' +
'</div>'

Using ng-switch in directive with transclude

I am trying to create a template that shows some transcluded content. When I use ng-show everything works fine, but using ng-if or ng-switch gives me problems. I get this error message: Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found
I understand that ng-switch creates a new scope. But the transclude should still go up to the parent chain. Is this a defect in angularjs? See http://jsfiddle.net/HgvP7/
Here is my html, modified from the documentation example:
<div ng-app="docsTransclusionExample">
<div ng-controller="Ctrl">
<my-dialog>Check out the contents, {{name}}!</my-dialog>
</div>
<!-- my-dialog.html -->
<script type="text/ng-template" id="my-dialog.html">
<div ng-switch="1+1">
<div ng-switch-when="2">
<div ng-transclude></div>
</div>
</div>
</script>
</div>
And the code:
angular.module('docsTransclusionExample', [])
.controller('Ctrl', function($scope) {
$scope.name = 'Tobias';
})
.directive('myDialog', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
templateUrl: 'my-dialog.html',
link: function (scope, element) {
scope.name = 'Jeff';
}
};
});

Nested directives don't work as expected

I have a generic directive
genericDirective
that is supposed to choose another specific directive
type1 directive if obj.type == "type1"
type2 directive if obj.type == "type2"
HTML
<div ng-controller="MainCtrl">
<div class="genericdirective" ng-repeat="obj in someArray"></div>
</div>
Javascript
var app = angular.module("myApp", []);
app.controller("MainCtrl", function ($scope) {
$scope.someArray = [
{type:"type1",title:"lorem"},
{type:"type2",title:"ipsum"},
{type:"type2",title:"dolor"}
];
});
app.directive("genericdirective", function(){
return{
restrict: "C",
template: "<div class='{{obj.type}}'>genericdirective</div>"
};
});
app.directive("type1", function(){
return{
restrict: "C",
template: "<div>type1</div>"
};
});
app.directive("type2", function(){
return{
restrict: "C",
template: "<div>type2</div>",
};
});
Output HTML
<div class="genericdirective ng-scope" ng-repeat="obj in someArray">
<!-- Not replaced by the actual directive -->
<div class="type1">genericdirective</div>
</div>
<div class="genericdirective ng-scope" ng-repeat="obj in someArray">
<!-- Not replaced by the actual directive -->
<div class="type2">genericdirective</div>
</div>
<div class="genericdirective ng-scope" ng-repeat="obj in someArray">
<!-- Not replaced by the actual directive -->
<div class="type2">genericdirective</div>
</div>
Any idea why these are not replaced by the actual directives?
By using the return in your genericDirective:
app.directive("genericdirective", function(){
return{
restrict: "C",
template: "<div class='{{obj.type}}'>genericdirective</div>"
};
});
You are returning the link function. The link phase happens after the compile phase. So, by the time you are resolving this template, angular cannot "compile in" your child directives and then link them.
You need to define a compile function and set up the directive at that time in order to modify the html that angular will consider. Any time that you need to manipulate the html before linking the $scope, you probably are wanting to make changes during the compile phase.
To read more about compile and link see the docs here. The section titled "Compilation process, and directive matching" is very helpful.
Building on Davin's answer, if you change your directive to this it should work:
app.directive("genericdirective", function($compile){
return{
restrict: "C",
link: function (scope, element, attrs) {
element.append('<div class="' + scope.obj.type + '">genericdirective</div>');
$compile(element.contents())(scope);
}
};
});

Resources