I know this is a subject discussed too many times. I know that jQuery may create problems. I know most sites advise try to avoid the use of jQuery, however my question is:
How may I manipulate the DOM inside an Angular directive if not using jQuery? Even in Angular docs there is intensive use of jQuery lite inside directives. I 'd appreciate your opinions in that.
In link function you can actually manipulate DOM.
app.directive('sampleDirective', function() {
return {
...
link: function(scope, ele, attr, ctrl) {
//here actually you can manipulate DOM using ele.
}
}
})
Angular has bilt-in jqLite(subset of jQuery). Find it here.
Note: Do DOM manipulations only in directive.
Related
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.
I have a ng-repeat-end-watch directive
.....
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
scope.$evalAsync(attr.ngRepeatEndWatch);
}
}
}
on my markup i have
<div ng-repeat="(i, tile) in tiles" class="w3-col tile tile-{{tile.number}}" ng-repeat-end-watch="ctlr.resize()" ng-model="tile">
and on my controller I have
rowsColumns = Math.round(Math.sqrt($scope.tiles.length)), widthHeight = $window.screen.availHeight/rowsColumns;
ctlr.resize = function(){
angular.element('.w3-col').width(widthHeight).height(widthHeight);
};
If in the developer's guide for AngularJS it recommends that you shouldn't use controllers to manipulate DOM elements
What would the recommended way be to apply the above "resize" if its not a good idea to use controllers to manipulate the DOM?
Angular is mostly for functionality, not direct DOM manipulation. You probably noticed that you access elements based on their model not id. You don't need any id in an an Angular app which makes it easier if certain html bits change. As long as the model stays the same you can do what you want with the html.
This being said if you do need to access the DOM you can still do it. You could have the code which does that outside of the controller in its own js file for example. Then you can inject it as an Angular service and reuse it in whichever controller you want.
I would try to keep things nicely separated and the controllers very thin. If you code it this way then it's easier to test things as you don't have to worry about controllers anymore. Hope this makes sense.
The best way to deal with it is writing your own directive. In fact, directives are one of the most powerful concepts in Angular.js.
Think of it as reusable components: not only for jQuery plugins, but also for any of your (sub) controllers that get used in multiple places. It’s kinda like shadow DOM. And the best part is, your controller does not need to know the existence of directives: communications are achieved through scope sharing and $scope events.
I'm writing an angular.js directive, which would conditionally hide the element.
So it would look like this:
link: function(scope, elem, attrs) {
...
elem.hide()
}
I found a lot of examples that were doing exactly that, but somehow my elem attribute is an array not an element, so it does not have a hide() method.
What am I missing?
Thanks!
Most people are loading jQuery before they load angular, which extends its jqLite to the full jQuery.
The hide method doesn't seem to be part of jqLite API (https://docs.angularjs.org/api/ng/function/angular.element), hence such method is not exposed.
That doesn't mean you need jQuery, but that it is not the correct way to handle your problem. There are already the ng-show and ng-if directives to conditionnaly hide an element based on the controller, couldn't you use them?
In your html, add <div ng-show="isDisplayed">, and in your linking function scope.isDisplayed = false
Our app is being ported from jQuery to AngularJS with bootstrap (angular-ui bootstrap).
One handy feature that was covered by the following excellent post was to add "http://" prefix to a URL field if it did not already have a prefix: http://www.robsearles.com/2010/05/jquery-validate-url-adding-http/
I am trying to achieve the same in AngularJS via a directive, but cannot get the directive to alter the value of the ng-model as it is being typed.
I've started simple by trying to get a fiddle to add a "http://" prefix on EVERY change for now (I can add the logic later to only add it when needed). http://jsfiddle.net/LDeXb/9/
app.directive('httpPrefix', function() {
return {
restrict: 'E',
scope: {
ngModel: '='
},
link: function(scope, element, attrs, controller) {
element.bind('change', function() {
scope.$apply(function() {
scope.ngModel = 'http://' + scope.ngModel;
});
});
}
};
});
Can anyone please help me to get this to write back to the ngModel. Also, the field I need to apply this new directive to already has a directive on it with isolate scope so I'm assuming I can't have another one with isolate scope - if this is so can I achieve it without isolate scope?
A good way to do this is by using the parsers and formatters functionality of ng-model. Many people use use ng-model as just a binding on isolated scope, but actually it's a pretty powerful directive that seems to lack documentation in the right places to guide people on how to use it to its full potential.
All you need to do here is to require the controller from ng-model in your directive. Then you can push in a formatter that adds 'http://' to the view, and a parser that pushes it into the model when needed. All the binding work and interfacing with the input is done by ng-model.
Unless I can find a good blog on this (very much open to comments from anyone who finds them), an updated fiddle is probably the best way to describe this, this support for URL to be entered manually with 'http' or 'https', as well as auto-prefixing if none of them: http://jsfiddle.net/jrz7nxjg/
This also solves your second problem of not being able to have two isolated scopes on one element, as you no longer need to bind to anything.
The previous comment provided by Matt Byrne doesn't work for the https prefix. Checkout the updated version based on previous answers that works with **https prefix too!
This was missing there
/^(https?):\/\//i
http://jsfiddle.net/ZaeMS/13
I'm trying to get a custom directive to work inside of ngRepeat, but can't get the obvious to work. In this case I don't 'believe' I want to isolate scope. I suspect this is simply a matter of framework ignorance, but can't seem to figure it out. I have a plunk here to show: http://plnkr.co/edit/LNGJHtbh7Ay0CYzebcwr
The link function runs only once for each instance of the sel directive, so it renders the arr.name value one time. In order to make it aware of future changes, you can use a $watch:
link: function(scope, elm, attr){
scope.$watch('arr.name', function() {
elm.text(scope.arr.name)
});
}
Plunker here.
You can find more information on that in the $rootScope documentation.