when to use link function and compile function in angularjs? - 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).

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: 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.

Altering innerText in an Angular directive

This seems to me like it should be straight-forward, but I think I'm misunderstanding the order of operations here. The documentation is a bit tough for me to digest and the answers I've found here have led me closer to an answer but not quite far enough.
I'm trying to place a scope variable (containing a string) onto the DOM using a directive and I want to manipulate that text and eventually create a "Read More" text truncation function.
HTML snippet:
<read-more>{{ commentary }}</read-more>
Angular/Coffeescript:
app.directive('readMore', [ ->
restrict: 'E'
scope: false
link: (scope, element, attrs) ->
console.log(element[0])
element[0].innerText = element[0].innerText.substring(0, 125) + "..."
])
The text from that variable gets read into the DOM, and console logs the element as <read-more ng-class="ng-binding"> and the {{ commentary }} string is printed in the console between the tags, however, my function doesn't manipulate it.
I know it's the correct element (and index), but for some reason innerText and innerHTML don't affect what is on the DOM.
If I change the return line in the link function to:
element[0].innerText = "Foo"
I get nothing between the <read-more> tags in the console and, naturally, the DOM now has no content in there.
What am I missing about how link deals with this element on the DOM?
My understanding is that element you are dealing here is not the JS element you are dealing for example in a standard jQuery function. This is angularjs element, Instead of DOM manypulation, I would rather create a directive that sets the model of the "Read More" element, and also hides the entire text that needs to be displayed after click on it. But do that only via angularjs ng-model directive, not by DOM manipulation.
You doesn't need DOM for angular.
Here show you a few ways to bind content:
http://plnkr.co/edit/OSWIy0?p=preview
Use this
link: function($scope) {
$scope.otherText = 'Here more text. For example, this maybe come from http-stream.';
}
instead this:
element[0].innerText = element[0].innerText.substring(0, 125) + "..."
Good luck. :-)
Angular provides a wrapper for the element using jQuery (if available) or jQLite, so use the html or text function:
link: (scope, element, attrs) ->
commentString = element.html() ## alternatively use element.text()
Note, however, that both the text and html functions will return the unevaluated string {{ commentary }} rather than whatever string value the commentary variable holds.
To get around this, simply address commentary using the scope argument passed to the directive's link function.
link: (scope, element, attrs) ->
commentString = scope.commentary
... ## perform string manipulation here
element.text(newCommentString)
This will set the element text to whatever string you pass to it. As for updating the text: if something like "read more" is clicked, have an event handler ready (still inside of link:, like so:
element.on('click', ->
element.text(commentString) ## the full commentary string from above
)

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

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

What is meant by AngularJS element?

I am trying to learn AngularJS. I was reading the https://github.com/angular/angular.js/wiki/Understanding-Directives : "You do NOT have to wrap AngularJS elements in jQuery()"
What is meant by an AngularJS element?
In Jquery, if I have a DOM element, say by using document.getElementByID() and if I want to make it a jquery element with access to all of its awesome functions, I will simply say
var domElement = document.getElementByID("elemID");
var $domElement = $(domElement); (To cache the element)
So, if in angular I dont have to wrap the DOM element as in Jquery, then can I simply query the DOM and access it and still be able to access all the angular methods.
If so, how it is done? Is it because of the ng-app binding?
Are all DOM elements inside ng-app internally accessed by Angular and turned to equivalent AngularJS elements?
The wiki page is talking about the elements passed by Angular as arguments to the compile and link functions of the directive:
link: function LinkingFunction($scope, $element, $attributes) { ... }
^-- here
If you get an element from the DOM using document.getElementById(), it won't be wrapped in jQuery automatically.

Resources