Using jQuery Instead Of jqLite In AngularJS
Out of the box, AngularJS provides jqLite, which is a jQuery-like library that provides a subset of jQuery functionality. If you include jQuery in your page, however, AngularJS will use jQuery instead of jqLite when wrapping elements within your directives. Until recently, however, I had only ever thought about this integration in terms of how the element references were presented; it never occurred to me that "angular.element" was actually an alias to the jQuery constructor function.
Run this demo in my JavaScript Demos project on GitHub.
This is not a post about whether or not you should be using jQuery in your AngularJS applications. I know that a lot of the "cool kids" are saying that if you use AngularJS, you don't need jQuery. Well, I'm a loner, Dottie, a rebel; personally, I think that jQuery adds a lot of value, especially with features like event delegation that facilitates deferred transclusion.
That said, this post is about what the jQuery-AngularJS integration looks like when you use it. Not only are your directive elements wrapped in jQuery containers, the angular.element() method is actually a direct reference to the jQuery() constructor function. This means that you have access to the full suite of jQuery features without needing a reference to the jQuery or "$" global references.
This means that angular.element contains all of the core jQuery functions, like jQuery.contains() and jQuery.proxy(). It also means that the "fn" prototype lives on the angular.element object, allowing jQuery plugins to be defined off of angular.element.
To explore this, I've created a small demo that uses an AngularJS directive that defines and consumes two new "jQuery" plugins. What you'll notice here is that I never make any reference to jQuery or to "$" - only to angular.element.
<!doctype html>
<html ng-app="Demo">
<head>
<meta charset="utf-8" />
<title>
Using jQuery Instead Of jqLite In AngularJS
</title>
<link rel="stylesheet" type="text/css" href="./demo.css"></link>
</head>
<body>
<h1>
Using jQuery Instead Of jqLite In AngularJS
</h1>
<p>
<em>Start clicking, bro.</em>
</p>
<ul bn-demo>
<!-- Dynamically populated. -->
</ul>
<!-- Load scripts. -->
<script type="text/javascript" src="../../vendor/jquery/jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="../../vendor/angularjs/angular-1.3.6.min.js"></script>
<script type="text/javascript">
// Create an application module for our demo.
var app = angular.module( "Demo", [] );
// -------------------------------------------------- //
// -------------------------------------------------- //
// I add an element to the point of a click and then randomly select one of the
// existing elements in the lsit.
app.directive(
"bnDemo",
function() {
// Create a jQuery plugin to help with the demo. Since we are including
// jQuery in our application, "angular.element" is a reference to the
// jQuery object, which means we can define plugins off the "element".
angular.element.fn.random = function() {
return( this.eq( Math.floor( this.length * Math.random() ) ) );
};
// Create a jQuery plugin to help set the position of the elements in
// the collection based on a pixel-based X/Y coordinates and an optional
// offset that is added to both the coordinate values.
angular.element.fn.xyo = function( x, y, offset ) {
this.css({
left: ( ( x + ( offset || 0 ) ) + "px" ),
top: ( ( y + ( offset || 0 ) ) + "px" )
});
return( this );
};
// Return the directive configuration.
return({
link: link,
restrict: "A"
});
// ---
// PUBLIC METHODS.
// ---
// I bind the JavaScript events to the local scope.
function link( scope, element, attributes ) {
element.on(
"click",
function handleClickEvent( event ) {
// If the user clicked on an existing LI, then don't change
// the contents of the container, just select the target.
if ( angular.element( event.target ).is( "li" ) ) {
// Select the target element.
element.children()
.removeClass( "selected" )
.filter( event.target )
.addClass( "selected" )
;
return;
}
// Create a new element at the click position.
angular.element( "<li></li>" )
.xyo( event.pageX, event.pageY, -25 )
.appendTo( element )
;
// Select a random element in the list.
element.children()
.removeClass( "selected" )
.random()
.addClass( "selected" )
;
}
);
}
}
);
</script>
</body>
</html>
As you can see, I am defining "jQuery plugins" on the angular.element.fn prototype, which is, of course, the jQuery fn prototype. Right now, I'm defining them inside of the directive factory; but, they might make more sense inside of an AngularJS .run() block so that they are a bit more reusable and a bit less coupled to the directive.
Again, this post isn't an argument for or against using jQuery in your AngularJS applications - that's a decision that should be made on a per-project basis. I happen to love jQuery and find it adds powerful features. But, regardless, I wanted to demonstrate that if you are using jQuery, the angular.element() method exposes the full array of jQuery functionality.
Want to use code from this post? Check out the license.
Reader Comments
Ben,
I understand what you're saying here, but I'm not sure about the helpfulness of it.
I am using jQuery (and jQuery UI) in the Angular based web app I'm building at work (I've never been a cool kid) , and I think it's better to use the jQuery syntax when using jQuery methods for clarity -- it's a marker that jQuery is being used in this spot. If, at some point, we decide to re-evaluate our use of jQuery, (unlikely, but you never know). we can go through and find the instances of it. That being said, it would probably be better if I used angular.element for those things that jqLite can handle on its own. :)
By the way, THANK YOU for all the help and information over the years. Your site was a great resource when I was working with ColdFusion, and now, you've been a big help as I've been trying to get up to speed with Angular in my new job.
@Ted,
First off, thank you very much for the kind words! Glad that some of this stuff is helpful from time to time - it only encourages me to keep on rambling :)
As for the jQuery / AngularJS integration, it's actually quite an interesting little problem. Since jqLite and jQuery present with the same interface inside directives, there's almost nothing that really can indicate which one is being used, unless you actually know which plugins are missing from the jqLite prototype.
For example, in my demo, I am using the .appendTo() method. This exists in jQuery, but NOT in jqLite. jqLite does, however, have .append() (which jQuery also has). So, the only thing that makes .appendTo() available is the fact that I'm including the jQuery library also.
So, fast forward a few months, and let's say I decide to remove jQuery from my application. It's not just a matter of looking for $() or $jQuery() references -- it's also a matter of looking for all uses of the jQuery functions that are not provided by jqLite (ex, .filter(), .appendTo(), .is(), .closest(), etc).
All to say, the integration of jQuery into AngularJS is so *subtle* that I think it has to be something very explicit. Meaning, you're either using jQuery or you're not and this never changes (without large, app-wide refactoring).
That said, like you, I still use jQuery, so this is not a battle I've ever had to deal with. As such, I'm definitely open to thinking about this differently.
Not sure I really addressed anything you said --- it's New Year's Eve, I'm a bit burnt out.
On a related note, this morning, I took this demo and refactored it exclude jQuery:
www.bennadel.com/blog/2753-creating-jqlite-plugins-in-angularjs.htm
As a result, I had to re-create the jQuery plugin in AngularJS as jqLite plugins. It was an interesting experiment.
@Ben,
The subtlety of the integration was what concerned me -- and that is what i was referring to when I said "it would probably be better if I used angular.element for those things that jqLite can handle on its own"; a confession that I'm not marking my own code as clearly as I could.
I guess I'm wondering what benefit I'd be getting by using angular.element in the manner you were describing that would be worth the blurring of the lines between the two systems. I'm relatively new to both jQuery and Angular -- up until a couple of years ago, all my code was just Plain Ol' JavaScript. :)
Happy New Year!
The post which you have done was very clear and useful for my search.
@Ted,
Right now, I'm kind of trying to figure out how much value-add jQuery has for AngularJS. I've been using jQuery for a good while now, so it's what I am used to. And, I do think it has things that angular.element doesn't have, necessarily. But, I'm wondering if the things I "think" jQuery offers are things that AngularJS can implement in different ways.
Example: event delegation vs. more granular linking:
www.bennadel.com/blog/2754-event-delegation-performance-vs-linking-performance-in-angularjs.htm
So, I guess that I am not sure there is a big "win" to making the line between AngularJS and jQuery thinner. I suppose I'm positioning it that way to try to better think about which parts are overlapping and which parts are jQuery-unique.
@Nisha,
Thank you very much!
Dude I love your posts. It seems like every time I look up little Angular intricacies such as this one (today), ng-include vs. custom directives (yesterday), or the various other times that Google has taken me here over the years for other JavaScript topics, you've got solid research-based facts on the matter.
Well done, good sir.
Thank you for this. I didn't understand how to use jQuery in an AngularJs application but now I finally cleared all my doubts :)
@Omar,
Thank you so much for the kind words! You just made my Friday :D And it's been a heck of a week!
@Reggie,
I'm glad this helped clarify. And, if you want to go in the other direction - using jqLite instead of jQuery, you might be interested in this post about writing jqLite plugins:
www.bennadel.com/blog/2753-creating-jqlite-plugins-in-angularjs.htm
It basically works the same way as writing jQuery plugins, only there is no .fn. short-hand, so you have to reference the jqLite.prototype object directly (which is what .fn is a short-hand for in jQuery).
At work, I'm having a battle with the angular developers to keep jQuery alive. I'm heavily rely on the jQuery UI for presentations and the developers say that you can't make Selectmenu (https://jqueryui.com/selectmenu/) work with dynamic content/items. Is this correct? Are you aware of any example showing a solution?
I am a back-end java developer and new to angular, in my project i am using an ready made template using jquery, i am using angular to connect to my restful web back-end, but the problem is i am loosing all the animation and visual effect of the template on including angular, moreover the template relies on many third-party jquery plugins, so what i need to do, i followed the steps from above but stuck, can you please suggest a way, how should i proceed with porting jquery codes into angular to make it workable?
@Dillip,
It's really hard to say what's going on without seeing more detail.
That being said, trying to 'port' jQuery code into Angular isn't really a direct thing. With jQuery, I find I'm much more focused on the DOM structure used to represent the information and less so on the data itself. Angular encourages you to focus more on the data itself, and let Angular generate the HTML. As I gain experience with Angular, I find myself "thinking the Angular way" more.
An example would be a pair of rating directives I wrote about a year apart. The first one, to display a store rating, is written mostly via DOM manipulation. The second, a control to set the store rating, was written about 6 weeks ago; in that one, I created a data structure, and let Angular create the rating HTML. Visually, they look nearly the same. But the newer one is more "Angular".
Getting back to your question, do you mean your new Angular code works, but the original code does not? If this is the case, do bear in mind that Angular supports a *subset* of jQuery. If you want access to the full jQuery API, you have to include jQuery first, then Angular. If you do this, then Angular will use the full jQuery, and it's also available to all the other plugins in the page.
Otherwise, the next thing I would ask is what are you using Angular for in the first place? Are you using it elsewhere? Do you need the two way data binding? Is this simply the first step in implementing it more deeply? If *all* you are using Angular for is communication with your back end, maybe you'd be better off using jQuery to do the Ajax requests, and to display your results.