Transclude directive fails to modify content - angularjs

I have defined a transclude directive as beliw in AngularJs 1.3 - but it does not seem to have any effect on the rendering.
A log statement in the link phase shows that the directive is invoked though.
index.html
<html lang="en" ng-app="directivesApp">
<head>
<script src="js/angular.min.js"></script>
<script src="js/app.js"></script>
</head>
<body ng-controller="directiveCtrl">
<label>Geography</label><input type="text" ng-model="geography"/>
<br>
<div transclude-demo>
<button ng-click='showGeography()'>Show Geography</button>
and a link
</div>
</body>
</html>
app.js
angular.module('directivesApp', [])
.controller('directiveCtrl',function($scope){
$scope.showGeography=function(){alert('I am here');}
})
.directive('transcludeDemo',function(){
return{
transclude: true,
template: '<div ng-transclude> This is my directive content</div>',
link:function ($scope, element, attrs) { console.log('invoked in scope');}
}
});
I would have expected the transclude directive to replace/modify the contents of the div,with the contents of its template.
However,I find that the div is rendered as-is.
Is this how a transclude directive is expected to work?

Transclude is used to preserve the content that's already there, so if you just want to replace the content all you really need is the template. You're not seeing much in your example because your containing divs are essentially the same.
Replace content:
.directive('transcludeDemo',function(){
return{
template: '<div>This is my directive content</div>',
link:function ($scope, element, attrs) { console.log('invoked in scope');}
}
});
If you'd like to combine the new/old content in some way, add something in your template outside of the ng-transclude and it will render in combination.
Combine with transclude:
.directive('transcludeDemo',function(){
return{
transclude: true,
template: '<div>' +
'<p>This text will stay in tact</p>' +
'<div ng-transclude>This text will be replaced</div>' +
'</div>',
link:function ($scope, element, attrs) { console.log('invoked in scope');}
}
});
As mentioned in the comments, this second example should give you a better understanding of what's actually happening.

Related

AngularJs transclude not working in Directive template or templateURL

I have written a custom directive like so, notice I have commented out the template URL that contains the same HTML structure and the template property:
.directive('sillyDirective', function ( ) {
'use strict';
return {
restrict: 'A',
replace: false,
transclude: true,
template: '<h2>Welcome to my site</h2>',
//templateUrl: '/views/hello.html' ,
link: function (scope, element, attrs) {
element.bind('click', function (){
alert('you click me! I am clicked');
});
};
});
In my HTML view I have the following...
<div data-silly-directive>
<div><img src="logo.jpg></div>
<div><h1>My First Website</h1></div>
</div>
The problem is the content of the directive, e.g.:
<div><img src="logo.jpg></div>
<div><h1>My First Website</h1></div>
is being overwritten with the template content even thought I have set transclude to true and replace to false? What am I doing wrong here?
you need to specify ng-transclude in the template of your directive, this will let angular know where to insert the content of the markup.
app.directive("foo", function() {
return {
transclude: true,
template: "<div>the template</div><div ng-transclude></div>"
};
})
html:
<div foo>
Some Content Here
</div>
result:
<div foo>
<div>the template</div>
<div ng-transclude>Some Content Here</div>
</div>
here's a plnkr
source: https://www.accelebrate.com/blog/angularjs-transclusion-part-1/
Your template must contain an element with an ng-transclude attribute. That's where the body will be "pasted" by angular.
See
https://docs.angularjs.org/api/ng/directive/ngTransclude

Transclude inside ng-repeat, controller function incorrect transclude param

I created directive that appends transclude value after the directive body. I cant use ng-transclude inside my directive because it creates for the simple text as transclude value and it break my page. I use
controller: function($scope, $element, $transclude) {
$element.append($transclude().contents());
}
to append it. It works great, but when I use my directive inside ng-repeat something goes wrong and $transclude().contents() don't contain my text. Can someone explain this behavior?
Here example:
http://plnkr.co/edit/1y4avkwmgjhiKkuoZlug
I can't explain why $transclude().contents() don't contain your text, but I can show how to easy fix that please code below.
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
});
app.directive('test', function(){
return {
transclude: true,
restrict: 'E',
template: '<div >From directive </div> <div ng-transclude></div><hr/>',
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="MainCtrl">
<div ng-repeat="a in [1, 2, 3, 4, 5]">
<test>From transclude {{a}}</test>
</div>
</body>

AngularJS - How to transclude into a script element?

See Plunker
I would like to transclude the content of the directive element into a <script> element.
In the below directive, transclusion does not work when the template contains a script tag as in the following:
app.directive('mdl', function(){
return {
restrict: 'EA'
, replace: true
, transclude: true
, template: '<script id="modal.html" > <div ng-transclude> </div> </script>'
}
})
where the directive is invoked as follows:
<mdlbody> <div>testing modal body</div> </mdlbody>
The resulting <main> element is empty. However, transclude works as expected when the <script> tag is removed from the template string like so,
template: '<div ng-transclude> </div>'
or template: <div> <div ng-transclude> </div> <div>
As long as the template string is not wrapped in <script> , the transclude works.
Transclude also works when ng-transclude is placed directly on <script ng-transclude>. However I want to insert the transclude at a deeper level of nesting inside the <script> element.
Why is this so? How can I get transclude to work with the <script> element in template string? (so that the directive's content is transcluded into the <script> element)
P.S: To clarify further, I am trying to transclude into the script element so the end result should be <script><main> transcluded content</main></script>
(not <main> transcluded content</main>).
if you are trying to refer to a template for this directive then replace template with templateurl because template expects some html code with angular directives write this:
app.directive('mdl', function(){
return {
restrict: 'EA'
, replace: true
, transclude: true
, templateUrl: 'modal'
}
})
and place the below lines at the end of your .htm file
<script type="text/ng-template" id="modal" > <main ng-transclude> </main> </script>

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';
}
};
});

Render a directive inside another directive (within repeater template)

I am trying to render a directive inside another directive (not sure if the repeater inside the template is working this), and it seems to just output as text rather than compiling the directive (plunker code here: http://plnkr.co/edit/IRsNK9)
Any ideas on how I can actually get it to render properly my-dir-one, my-dir-two, my-dir-three directives inside the repeater?
index.html
<!doctype html>
<html ng-app="plunker" >
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<link rel="stylesheet" href="style.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.js"></script>
<script src="app.js"></script>
<script id="partials/addressform.html" type="text/ng-template">
partial of type {{type}}<br>
</script>
</head>
<body>
<div container></div>
<br /><br /><br />
<b>Below is just to test the directives are actually usable outside the repeater</b>
<div my-dir-one></div>
<div my-dir-two></div>
<div my-dir-three></div>
</body>
</html>
app.js
var app = angular.module('plunker', []);
app.directive('container', function () {
return {
restrict: 'A',
scope: {},
replace: true,
template: '<div class="views">' +
' <div class="view" ng-repeat="view in views">' +
' <div {{view.dir}}>{{view.dir}}</div>' +
' </div>' +
'</div>',
link: function (scope, elm) {
scope.views = [
{ dir: 'my-dir-one' },
{ dir: 'my-dir-two' },
{ dir: 'my-dir-three' }
];
}
}
});
app.directive('myDirOne', function () {
return {
restrict: 'A',
scope: {},
replace: true,
template: '<div>This is directive one.</div>'
}
});
app.directive('myDirTwo', function () {
return {
restrict: 'A',
scope: {},
replace: true,
template: '<div>This is directive two.</div>'
}
});
app.directive('myDirThree', function () {
return {
restrict: 'A',
scope: {},
replace: true,
template: '<div>This is directive three.</div>'
}
});
I managed to work around this issue by re-writing the code:
First I updated the template code as follows:
template: '<div class="views">' +
' <div class="view-wrapper" ng-repeat="view in views">' +
' <div view="{{view.dir}}"></div>' +
' </div>' +
'</div>',
Note that I created a new 'view' directive. Next the view directive definition as follows:
app.directive('view', ['$compile', function (compile) {
return {
restrict: 'A',
scope: {
view: '#'
},
replace: true,
template: '<div class="view"></div>',
controller: ['$scope', function (scope) {
scope.$watch('view', function (value) {
scope.buildView(value);
});
}],
link: function (scope, elm, attrs) {
scope.buildView = function (viewName) {
var view = compile('<div ' + viewName + '></div>')(scope);
elm.append(view);
}
}
}
}]);
So essentially, the view.dir variable is passed as an attribute to the 'view' directive, which then watches it's value and compiles a template with the directive in it.
This is in part a timing problem...I think that by the time it's resolving the {{}} expressions, it's already parsed out and rendered directives. It's not the nesting or the repeater that are the problem, per se.
What you're after here, though, is 'decide which directive to render based on the value of a variable'. There are a couple ways to do that.
Here's one that should work, though it might not scale as nicely as you'd like:
<div class='views' ng-repeat='view in views'>
<div ng-switch='view.dir'>
<div ng-when='my-dir-one' my-dir-one />
<div ng-when='my-dir-two' my-dir-two />
<div ng-when='my-dire-three' my-dir-three />
</div>
</div>
Other options for similar tricks: it looks like you could use ngBindTemplate to take a string from your data and use it as the template for an element. This would probably permit some tricky (and illegible) behavior.
You can specify a directive for an element as a class, but I don't know whether using ngClass to do this would allow you to dynamically select the directive, or whether that would come too late in the pipeline.

Resources