What is meant by AngularJS element? - angularjs

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.

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.

Dynamically created element not firing click event

$scope.addNew = function(){
$('.thumb-img-gallary').append("<li><span class='img-del' ng-click='delThumbImgGallaryPhoto($event)'>X</span><img class='thumb-img' src='data:image/jpeg;base64,"+imageData+"'/></li>");
}
I am calling this function to add element dynamically. But then delThumbImgGallaryPhoto() is not getting called.
you cannot just append an element with a ng-click or any other directive, and expect it to work. it has got to be compiled by angular.
explenation about compilation from angular docs:
For AngularJS, "compilation" means attaching directives to the HTML to make it interactive
compelation happens in one of two cases:
When Angular bootstraps your application, the HTML compiler traverses the DOM matching directives against the DOM elements
when you call the $compile service inside an angular context
so what you need to do, is first to compile it(remeber to inject the $compile service in your controller), and then append it:
$scope.addNew = function(){
var elem = $compile("<li><span class='img-del' ng-click='delThumbImgGallaryPhoto($event)'>X</span><img class='thumb-img' src='data:image/jpeg;base64,"+imageData+"'/></li>")($scope);
$('.thumb-img-gallary').append(elem);
}
BTW, remember it is prefable not to have any DOM manipulations done in the controller. angular has directives for that.
You have to compile it with scope
try like this
var item="<li><span class='img-del' ng-click='delThumbImgGallaryPhoto($event)'>X</span><img class='thumb-img' src='data:image/jpeg;base64,"+imageData+"'/></li>"
$('.thumb-img-gallary').append(item);
$compile(item)($scope);
angular doesn't know anything about your newly added element. You need to compile your newly added element using $compile. and you should better use directive for this task.
It is a bad habit to access ui elements from controller.
edit: it would be best using ng-repeat for this task. lets say you have a thumb-gallery directive which is repeated using ng-repeat by thumbs array.
when you need to add a new image you only need to add it to your thumbs array.
it is simple and straightforward
Your html would look like
<thumb-gallery ng-repeat="gallery in galleries"></thumb-gallery>
and your js would look like
var gallery = {};
$scope.galleries.add(gallery);

Access Angular Controller Defined Element

Is there a way to access the div which is in the controller div or the controller defined div without defining them with and class or id JUST using the $scope.
<div ng-controller="gridController">
<div></div> // < -- I want to access this element
</div>
To be a bit more specific does angular saves and gives access to the element DOM info which the ng-controller was called ?
A controller has no concept of the DOM, and it should stay that way or you run the very likely risk of writing untestable code. This is a part of the separation of concerns in the angular framework. A controller can be bound to multiple different elements or even to the controller function of different directives and there would be know way to tell them apart.
If you are attempting to do anything to the DOM you should be using a directive.
Given more information about what you want to accomplish with the element in question more guidance to reach your goal could be given.
You can access the element the controller is defined on by injecting the angular variable $element into your controller
angular.module('myApp')
.controller('SomeCtrl',['$scope', '$element', function($scope, $element) {
$scope.buttonClick = function () {
$element.find('div').css('background-color', '#f00');
}
}]);
If jQuery is included in your project, $element will be a jQuery object of the element your controller is defined on (the outer div in your example). You can use standard jQuery directives to access its sub elements. Accessing an unnamed child div would require you to know its position, or use some other criteria to target it.
If jQuery is not installed, angular includes jqlite which is a subset of jQuery allowing you to use most jQuery selectors to target elements, but lacking most of the other manipulation features.
However, it is generally considered bad practice to access, and especially manipulate the DOM from within a controller. Because of the way jQuery binds variables to html, if you make changes to the DOM from certain functions, angular may not pick these changes up and overwrite them when it next does a binding cycle. To avoid this, you should do most of your DOM manipulation from inside an angular directive. Sometimes however, its just easier to do it from the controller... If you are going to do this, you should learn about $apply and $digest.

AngularJS -> Capturing when DOM has finished rendering elements in directive

We're using AngularJS to render a schedule table with ng-repeat.
I also need to call an initialization function for a jQuery plugin that acts on the DOM objects being rendered by the Angular code.
However, I do not know how to make it call this initialization until AFTER the DOM objects have been created because the plugin requires the DOM elements to be built before initialization.
How can I do that?
Can use $timeout which won't run until element exists
app.directive('makeMeGreen',function($timeout){
return{
restrict:'A',
link:function(scope,elem,attrs){
$timeout(function(){
elem.makeMeGreen();
},0)
}
}
});
DEMO
Alternate approach is to $observe attributes

Massaging the DOM with AngularJS

I know manipulating the DOM goes against the rules of Angular but in this case, am having do transverse the DOM to modify a sibling node.
In jQuery you can do something like this:
$(this).parent().addClass('.loading');
While in Angular you would do something like this:
angular.element(this).parent().addClass('.loading');
Certainly this doesn't work because there is no parent() method or addClass() method support on the API.
Which brings me to the question, how else can I accomplish this?
Thanks!
Angular elements are wrapped with jqLite by default (Angular's own jQuery implementation). If you have added jQuery to your project, then elements are wrapped with full jQuery.
Here's a list of methods available with jQuery lite http://docs.angularjs.org/api/angular.element
As you can see, you have access to parent() and addClass() So you get a lot of DOM manipulation power w/o adding jQuery as a dependency.
-*-
It's perfectly fine to manipulate the DOM with angular, the best practice is to do it from directives, here's a little example of an element having access to the parent element
HTML
<div ng-app='app'>
<div id="someparent">
<div my-directive>
</div>
</div>
</div>
In your JS
var app = angular.module('app', []);
app.directive('myDirective', function(){
return{
restrict: 'A',
link: function(scope, element, attributes){
console.log(element.parent().attr('id')); // "someparent"
element.parent().addClass('loading'); // adding loading class to parent
}
};
});​​​​​​​​​​​​​
jsfiddle: http://jsfiddle.net/jaimem/Efyv4/1/
Of course when building your app you might want to have directives manipulating only elements within itself.
-*-
Also, as Mark mentions, you can use angular's existing directives such as ngClass instead of creating your own. It's not clear what you want to achieve, but I recommend looking at ngCloak.
In jQuery, we often have some event trigger, then from the element that triggered the event, we traverse the DOM to find some other element which is then manipulated. E.g., your example:
// some event causes this code to execute:
$(this).parent().addClass('.loading');
In AngularJS, we declare where the manipulation points are in HTML first (e.g., ng-class as #blesh suggests), then events modify $scope properties, and Angular does the DOM manipulation automagically for us. E.g.:
<div ng-controller="MyCtrl" ng-class="loadingClass">parent
<a ng-click="loadingClass='loading'">child</a>
</div>
Controller:
function MyCtrl($scope) {
// if you want some other class initially, uncomment next line:
// $scope.loadingClass = 'notLoading';
}​
Above, clicking the "child" link modifies $scope.loadingClass. Angular will notice the change and apply the new class to the parent div.
For more complicated manipulations, a custom directive like #jm- shows would be required.
jQuery: events drive DOM traversal and DOM manipulation code (imperative).
Angular: events modify $scope and the magic happens (on our declarative HTML).

Resources