AngularJS Link function: difference between element.attr and link's attrs? - angularjs

In AngularJS's link function we have link: function(scope, element, attrs). What is the difference between element.attr() and the link's attrs?

Take a look at this DEMO:
app.directive("test",function($compile){
return {
restrict:"A",
link:function(scope,element,attrs){
console.log("attrs:" + attrs.name);
console.log("element.attr:" + element.attr("name"));
}
};
});
attrs.name: display the resolved value.
element.attr("name"): display the original expression
Basically, attrs displays the value resolved based on current scope, element.attr() displays the original string assigned to the HTML

The element is a jqlite or jquery object which has a method attr
Whereas attrs defined by Angular documentation is
iAttrs - instance attributes - Normalized list of attributes declared
on this element shared between all directive linking functions.
See compile documentation for Attributes https://docs.angularjs.org/api/ng/service/$compile

Related

Angular JS manipulating dom by element from link function

we know that we can access dom from directive by element because element is injected in link function.
see the approach
var app = angular.module("myApp", []);
app.directive('busyBox',function(){
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.on('click', function(){
if(attrs.id=='btnadd')
{
var divElement = angular.element(document.body.querySelector('.parent')).append('<div class="child">Some text</div>');
// console.log(divElement);
// element.parent().find('.parent').append('<div>Some text</div>')
//element.closest('.parent').append('<div class="child">child</div>')
//angular.element(document).find('.parent').append('<div class="child">child</div>');
}
else if(attrs.id=='btnDel')
{
angular.element(document.body.querySelector('.child')).remove();
// m.removeChild(m.firstChild);
}
});
}
}
})
the above code is working but if i do not use angular.element() instead if i use element(document.body.querySelector('.parent')) the code is not working.
the element is injected in link function link: function(scope, element, attrs)
when element is there in directive then why should i use angular.element() ?
please tell me how could i use element from directive to access dom instead of angular.element().
thanks
The element exposed to the postLink function is a jqLite class object which is a tiny, API-compatible subset of jQuery that allows AngularJS to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most commonly needed functionality with the goal of having a very small footprint.
The find() method is limited to lookups by tag name. If you want the find() method to work with class selectors, load jQuery before the angular.js file.
If jQuery is available, angular.element is an alias for the jQuery function. If jQuery is not available, angular.element delegates to AngularJS's built-in subset of jQuery, called "jQuery lite" or jqLite.
To use jQuery, simply ensure it is loaded before the angular.js file. You can also use the ngJq directive to specify that jqlite should be used over jQuery, or to use a specific version of jQuery if multiple versions exist on the page.
— AngularJS angular.element API Reference
Can't we use element. closest function instead of find to get the div?
The jQuery closest function is not part of AngularJS jqLite. To use the closest function with the element value exposed to the directive postLink function, load the jQuery library before loading the AngularJS library.
For the list of jqLite functions, see AngularJS angular.element API Reference
please tell me how could i use element from directive to access dom instead of angular.element()
If you want to use the element object that is passed into the link function of the directive to manipulate the DOM, you have to use various methods that the element object exposes, like find.
Do not use it like this:
element(document.body.querySelector('.parent'))
Instead you can do:
// works only if you are using jQuery
element.find('.parent');
// for JQLite, the find method is limited to lookup by tag name:
element.find('div');
Note that as mentioned in the answer from #georgeawg, that you might be using JQLite or JQuery ... where the JQLite functionality is not as full featured as JQuery.

AngularJS Directives - why do I need to call $compile in link() for expression defined in compile()?

I have this code:
app.directive('foo', function($compile) {
return {
restrict: 'E',
scope: {},
template: '<span>{{bar}}</span>',
compile: function(element, attrs) {
element.attr('title', '{{bar}}');
return function(scope, element, attrs) {
scope.bar = 'hello';
$compile(element)(scope);
}
}
}
});
Plunkr:
http://plnkr.co/edit/nFTgvYqoiFAthmjoizWS?p=preview
If I remove the $compile bit in the link function then the title attribute remains with the expression text ({{bar}}) and not the value ('hello');
Anyone can explain why?
I thought (from what I read in the docs) that this is what the compile phase is for - manipulating the template and preparing it for the link with scope and data binding. Why do I need to manually call $compile again? Isn't the template already compiled?
Maybe the phase names should be changed from compile, preLink, and postLink to postCompile, preLink, and postLink. The postCompile phase is availble to manipulate DOM before linking to a scope, at this point the linking function has been created but no scopes have been created. DOM can be added that requires no compilation. If additional elements are added that include directives or require interpolation, those additional elements need to be compiled and linked in order for the directives and interpolation to work.
To manupulate the template before compile, furnish a function to the template property: template: function(tElement, tAttrs) {}. For more information, see AngularJS Comprehensive Directive API Reference -- Template.
can you share a reference to "DOM can be added that requires no compilation, etc." or explain how did you found out about this?
Some sources of information:
AngularJS Developers Guide -- HTML Compiler
AngularJS Developers Guide -- Creating a Directive that Manipulates the DOM

when to use link function and compile function in angularjs?

I am still confuse where to use link and compile function.
Thanks in advance!
The link function is used in a directive.
The params of the link function reference the element the directive is attached to.
link: function(scope, element, attr)
Element here is the HTML element. and attr refers to the attributes of the HTML element
So you could do element.text('fred');
To set the text of the element to 'fred' and so on.
$compile I am not so familiar
From this SO Answer
compile function - use for template DOM manipulation (i.e.,
manipulation of tElement = template element), hence manipulations that
apply to all DOM clones of the template associated with the directive.
link function - use for registering DOM listeners (i.e., $watch
expressions on the instance scope) as well as instance DOM
manipulation (i.e., manipulation of iElement = individual instance
element).

Angularjs: how to clone an element, uninterpolated

I have a directive that clones its element, passes the clone through $compile(myClone)(scope); and appends it to the DOM.
If the original element has transcluded content, ex: This is some content {{withVariable}}
How can I clone it with the angularjs expressions uninterpolated, so that the cloned element has the expressions (and thus the same dynamic content as the original), rather than the values as resolved at the time of cloning?
If I use ng-bind directive, it work as desired.
ex. This is some content <span ng-bind="withVariable"></span>
Ok, I found a solution using transclude: true on my directive.
And then I have in the link function:
link: function (scope, element, attrs , uiScrollpoint, transclude ){
transclude(scope, function(clone, scope){
// stash the un-interpolated innerHTML
element[0].srcHTML = clone[0].innerHTML;
element.append($compile(clone)(scope));
});
}
When I clone the element, I retrieve the srcHTML before compiling:
var myClone = element.clone();
if(element[0].srcHTML){
myClone[0].innerHTML = element[0].srcHTML;
}
$compile(myClone)(scope);
It seems to work, but I do see the limitation that if the original element's source is modified on the fly by JS DOM-manipulation functions that srcHTML wouldn't stay in sync. I'm thinking that this would be a pretty rare case though.
Maybe there is a better way to do this? If it's possible to get the uninterpolated HTML at clone time rather than only at transclusion time, that would really be the best.

custom Directive replace element not working

I have a custom directive that generates an input with its validation errors , eventually after building the input, here is the part I'm wondering about :
var html= template.get();
element.html(html);
$compile(element)(scope);
I also added to the directive object which I think is not making difference since I don't have template in the object or should it?
replace:true
but yet the directive element DOM is still there , and the generated input is being appended as a child , can you help me out in this ?
Yes, replace is used in conjunction with template/templateUrl.
For the templates that are retrieved dynamically in link, use
var html= template.get();
element.replaceWith($compile(html)(scope));
Notice that obvious drawback in comparison with replace is that directive's attributes won't be translated to template element, this has to be done manually.
Its not working because you haven't provided a template parameter and your link function was manually adding the elements. Remove the link function and add a template parameter like so and it'll work. Ive updated your fiddle with it working too
app.directive('test',function($compile){
return {
restrict:'E',
replace:true,
template:'<input type="text" name="test">'
}
});

Resources