What is ng-transclude? - angularjs

I have seen a number of questions on StackOverflow discussing ng-transclude, but none explaining in layman's terms what it is.
The description in the documentation is as follows:
Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
This is fairly confusing. Would someone be able to explain in simple terms what ng-transclude is intended to do and where it might be used?

Transclude is a setting to tell angular to capture everything that is put inside the directive in the markup and use it somewhere(Where actually the ng-transclude is at) in the directive's template. Read more about this under Creating a Directive that Wraps Other Elements section on documentation of directives.
If you write a custom directive you use ng-transclude in the directive template to mark the point where you want to insert the contents of the element
angular.module('app', [])
.directive('hero', function () {
return {
restrict: 'E',
transclude: true,
scope: { name:'#' },
template: '<div>' +
'<div>{{name}}</div><br>' +
'<div ng-transclude></div>' +
'</div>'
};
});
If you put this in your markup
<hero name="superman">Stuff inside the custom directive</hero>
It would show up like:
Superman
Stuff inside the custom directive
Full example :
Index.html
<body ng-app="myApp">
<div class="AAA">
<hero name="superman">Stuff inside the custom directive</hero>
</div>
</body>
jscript.js
angular.module('myApp', []).directive('hero', function () {
return {
restrict: 'E',
transclude: true,
scope: { name:'#' },
template: '<div>' +
'<div>{{name}}</div><br>' +
'<div ng-transclude></div>' +
'</div>'
};
});
Output markup
Visualize :

For those who come from React world, this is like React's {props.children}.

it's a kind of yield, everything from the element.html() gets rendered there but the directive attributes still visible in the certain scope.

Related

Different scope when using ng-repeat and custom directive in one element

I have a directive like:
angular.module('myApp').directive('myDirective', function() {
return {
templateUrl: '/views/myView.html',
restrict: 'E',
link: function (scope, element, attrs) {
console.log(scope);
console.log(angular.element(element).scope());
}
};
});
the template html is:
<div>{{item.text}}</div>
and view html is:
<div ng-init="items=[{text:'hello'}, {text: 'world'}]">
<my-directive ng-repeat="item in items"></my-directive>
</div>
I find the scope and angular.element(element).scope() is not the same one, but I have to use angular.element(element).scope() way to get the scope of ngRepeat-item somewhere else.
Am I misunderstanding something?
Note:
The code could be reproduced only when including jQuery.
Here is related github issue.
The main reason is: angular1.2.x does not support jQuery2.0.x, and angular1.3.x will do.
So the solution here will be:
angular1.2.x + jQuery 1.x
angular1.3.x + jQuery 2.x
angularjs only

AngularJS directive retaining original content

I'm totally confused on transclude/replace and directives. I thought I understood but now I'm lost on how to get this particular test case working. From everything I've in the docs, SO, and blogs, it seems like my code below should work. First, some code:
The markup:
<my-directive><h1>My Title</h1></my-directive>
The end result I want:
<my-directive><div class="awesome"><h1>My Title</h1></div></my-directive>
The directive:
myApp.directive('myDirective', function() {
return {
restrict: 'E',
transclude: true,
template: '<div class="awesome"></div>'
}
});
Instead of what I want, I get:
<my-directive><div class="awesome"></div></my-directive>
What am I doing wrong?
To place the transcluded content into the div, add the ng-transclude directive to it...
template: '<div class="awesome" ng-transclude></div>'
Demo - Fiddle
You are missing the "ng-transclude" in the directive template:
<div class="awesome" ng-transculde></div>

Access repeated item inside ng-repeated directive

I'm building a custom directive in AngularJS that I need to be repeated a couple of times. Currently, my page looks like this:
<div my-item ng-repeat="item in items" />
And my directive looks like this:
module.directive('myItem', function() {
return {
restrict: 'A',
replace: true,
scope: { item: '&' },
template: '<div id="item{{$index}}"></div>',
link: function($scope, element, attributes) {
element.append('<div>' + $scope.item.name + '</div>');
}
};
});
However, inside the linking function, $scope.item.name yields undefined. I'm wondering if there is any way I could access the repeated item inside my directive.
If not, what would be my alternatives? Move the ng-repeat inside the directive, maybe?
P.S. I know that you should (generally speaking) not do DOM manipulation this way, but since I might have ~2000 items that would result in 6000 bindings, and I'm afraid that would lead to severe performance issues.
You should pass item as attribute to directive created a sample directive
http://plnkr.co/edit/l5r6zIc7ncT1XldRuB98?p=preview

AngularJS what does locals in directive stand for

AFAIK this is not documented, but I found in angular source a locals attribute in a directive example:
angular.module('transclude', [])
.directive('pane', function(){
return {
restrict: 'E',
transclude: true,
scope: 'isolate',
locals: { title:'bind' },
template: '<div style="border: 1px solid black;">' +
'<div style="background-color: gray">{{title}}</div>' +
'<div ng-transclude></div>' +
'</div>'
};
});
What does it do? How can I use it?
EDIT
to be more precise:
How can I access locals from directive's controller or linking function?
How can I dynamicly change locals from directive's controller or linking function?
Can I use locals in every directive, or does it have to be a directive with a transclude=true ?
The example code is on the ngTransclude page, inside the script.js tab.
I believe this is just the older syntax (which still seems to work). The newer syntax would replace
scope: 'isolate',
locals: { title:'bind' },
with
scope: { title: '#' },
I just want to close this question.
So the answer is like #ArunPJohny said
#param {Object=} locals (Optional object).
If preset then any argument names are read from this object first, before the $injector is consulted

ng-repeat in combination with custom directive

I'm having an issue with using the ng-repeat directive in combination with my own custom directive.
HTML:
<div ng-app="myApp">
<x-template-field x-ng-repeat="field in ['title', 'body']" />
</div>
JS:
angular.module('myApp', [])
.directive('templateField', function () {
return {
restrict: 'E',
compile: function(element, attrs, transcludeFn) {
element.replaceWith('<input type="text" />');
}
};
});
See jSFiddle
The problem here is that nothing is replaced. What I'm trying to accomplish is an output of 2x input fields, with the 'x-template-field' tags completely replaced in the DOM. My suspicion is that since ng-repeat is modifying the DOM at the same time, this won't work.
According to this Stack Overflow question, the accepted answer seems to indicate this has actually worked in earlier versions of AngularJS (?).
Wouldn't element.html('...') work?
While element.html('...') actually injects the generated HTML into the target element, I do not want the HTML as a child element of the template tag, but rather replace it completely in the DOM.
Why don't I wrap my template tag with another tag that has the ng-repeat directive?
Basically, for the same reason as above, I don't want my generated HTML as a child element to the repeating tag. While it would probably work decently in my application, I would still feel like I've adapted my markup to fit Angular and not the other way around.
Why am I not using the 'template' property?
I haven't found any way to alter the HTML retrieved from the 'template' / 'templateUrl' properties. The HTML I want to inject is not static, it's dynamically generated from external data.
Am I too picky with my markup?
Probably. :-)
Any help is appreciated.
Your directive needs to run before ng-repeat by using a higher priority, so when ng-repeat clones the element it is able to pick your modifications.
The section "Reasons behind the compile/link separation" from the Directives user guide have an explanation on how ng-repeat works.
The current ng-repeat priority is 1000, so anything higher than this should do it.
So your code would be:
angular.module('myApp', [])
.directive('templateField', function () {
return {
restrict: 'E',
priority: 1001, <-- PRIORITY
compile: function(element, attrs, transcludeFn) {
element.replaceWith('<input type="text" />');
}
};
});
Put your ng-repeat in the template. You could modify attributes of element and accordingly in directive to determine if ng-repeat is needed, or what data to use inside the directive compiling
HTML(attribute):
<div ng-app="myApp" template-field></div>
JS:
angular.module('myApp', [])
.directive('templateField', function () {
return {
restrict: 'A',
template:'<input type="text" value="{{field}" ng-repeat="field in [\'title\',\'body\']" />'
};
});
DEMO: http://jsfiddle.net/GDfxd/3/
Also works as an element :
HTML(element):
<div ng-app="myApp" >
<template-field/>
</div>
JS
angular.module('myApp', [])
.directive('templateField', function () {
return {
restrict: 'E',
replace:true,
template:'<input type="text" value="{{field}}" ng-repeat="field in [\'title\',\'body\']" />'
};
});
DEMO: http://jsfiddle.net/GDfxd/3/

Resources