How does angular determine whether jQuery is present? - javascript

Because I'm getting the weird situation that angular objects like element in the link function of a controller ends up being a jQLite object although jQuery is definitely present as in being loaded in memory and successfully used elsewhere on the same page.
The Angular FAQ is rather vague on that question:
Yes, Angular can use jQuery if it's present in your app when the application is being bootstrapped. If jQuery is not present in your script path, Angular falls back to its own implementation of the subset of jQuery that we call jQLite.
So what exactly does 'present' mean?

Angular.js will use jQuery if it's included first, otherwise it will use it's own jqLite. If you load jQuery after AngularJS, AngularJS attaches itself to jqLite but you can still access jQuery through $.
See below the code used by angular.js to determine if jquery is loaded:
// bind to jQuery if present;
jQuery = window.jQuery;
// Use jQuery if it exists with proper functionality, otherwise default to us.
// Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
// Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
// versions. It will not work for sure with jQuery <1.7, though.
if (jQuery && jQuery.fn.on) {
jqLite = jQuery;
extend(jQuery.fn, {
scope: JQLitePrototype.scope,
isolateScope: JQLitePrototype.isolateScope,
controller: JQLitePrototype.controller,
injector: JQLitePrototype.injector,
inheritedData: JQLitePrototype.inheritedData
});
Changing the order of script tags may not happen very often, but it could occur if you start modularising the codebase. In particular, this issue has happened while using some module loaders like RequireJS.

Related

Angular jqLite on $destroy not working

I have a div called MyDiv and am bootstraping Angular manually to it (due to some limitations from a legacy system). In a particular case, I would like that on $destroy I do some further processing. I am using the below snippet, however it seems that it doesn't trigger anything - the console logging is not working :(
angular.element(document.querySelector('#MyDiv')).on('$destroy', function() {
// do something
console.log("test");
});
Can someone tell me what I am doing wrong? This is the first time I am relying on jqLite as usually I use the full jQuery. I am using this in a controller not a directive.
Additional Detail:
I've also tried using the below, however I get this error in the console: Error: [jqLite:nosel] Looking up elements via selectors is not supported by jqLite!. This used to work when I was loading AngularJS after jQuery but i had to change the order to load angular first, and therefore have to rely on jqLite.
angular.element('#MyDiv').on('$destroy', function() {
alert("Test");
});
Note - if there is a way to do this using native js that is fine.
Thanks

$ not working for jQuery in new website

I have been using jQuery for quite a few years now, and after I include the jQuery scripts in whatever site I am working on, I would always use the $ for jQuery objects. For example:
$('#my_selector').click(function(){...
I had built a website a few years ago in Joomla 1.6 with over 200 pages and jQuery used in almost all of them, all with the $. Now I am rebuilding the site in Joomla 3.3.0. The funny thing is, now sometimes the $ just doesn't work when identifying jQuery objects, but when I use jQuery it works. For example. the above code example would have to be changed to this:
jQuery('#my_selector').click(function(){...
And that works. And the final strangest thing is that on one page, it seems like the $ works for some of the jQuery but not all. The error that I see is this one:
TypeError: undefined is not a function
Seems like the problem occurs mostly on the functions that run after load complete circumstances. Anyway I am just wondering if people out there know why the $ would stop working with identifying the jQuery functions and objects.
Thanks!
You are most probably using a conflicting library, meaning: another script/library that declares (and thus overrides) the variable $. Wrap all your code in a closure, and you should be good:
(function($) {
$('#my_selector').doStuff();
})(jQuery)
Or, if it needs to be executed after document ready:
jQuery(document).ready(function($) {
$('#my_selector').doStuff();
});
Joomla 3.x is moving progressively to jQuery and replacing all MooTools dependencies along the way.
The default state is to load jQuery in noConflict() mode, but depending on features used on any given page by extensions (templates,plug-ins, components or modules) MooTools may also be loaded.
That means that on some pages, jQuery is defined and not $ and on other pages both are defined, obviously this will result in the issues you are seeing.
Add to that most third-party extensions from the 1.6 era (you have been upgrading to the 2.5.x line along the way right?) just ignored what-ever was going on and loaded whatever they needed (potentially blowing away other libraries) you generally will have to sort out all the conflicts first.
The only guaranteed way to use jQuery is by using the jQuery prefix.
You can read about using JavaScript frameworks with Joomla here, amongst other things it gives you future proof mechanism built-in to Joomla for loading jQuery.
To load jQuery, use: JHtml::_('jquery.framework');
To load the jQuery UI core call: JHtml::_('jquery.ui');
As has been mentioned you can wrap your JavaScript in a closure, in fact this is what the core com_banners does in /media/cbanner.js
var jQuery;
(function ($) {
$(document).ready(function () {
$('#jform_type').on('change', function (a, params) {
var v = typeof(params) !== 'object' ? $('#jform_type').val() : params.selected;
switch (v) {
case '0':
// Image
$('#image, #url').show();
$('#custom').hide();
break;
case '1':
// Custom
$('#image, #url').hide();
$('#custom').show();
break;
}
});
});
})(jQuery);
Are you or joomla using mootools or any other library with the $?
This would mean there is a conflict and the right way to solve it, is by using jQuery instead of $.

Rails/Javascript selectors won't find elements

I am in the process of installing the asset pipeline into an older rails app. I am getting some really strange results though. I can see that the page is rendering all of the css and the jquery that is in the app/assets directory but its having a hard time interacting with the html.
For instance if i inspect the page and in the console call $("html").html(); to try grab all the html it returns TypeError: Cannot call method 'html' of null same when trying to grab any element that is being rendered? but the page is there. if i call jQuery it will return fine. so its not like it jQuery isnt there.
$ is just a shorthand way of writing jQuery. If the latter works but the former doesn't, then another script in your pipeline is probably conflicting with jQuery and trying to use the $ symbol for something else.
Are you using any other plugins or libraries that might be trying to use $? Or have you accidentally overwritten it yourself by writing $ = (something) anywhere? Without more information it's hard to know where the problem is exactly.
If all else fails you can just stick to using jQuery() for all your calls. In your external script file you could also circumvent this by passing the jQuery object to a wrapper function, e.g.:
(function ($) {
$('div').append('You can use $ here without having to worry about conflict.');
}(jQuery))

Export Angular JQLite as $/jQuery

I occasionally find plugins that try to detect if JQuery is present, or might have 1-2 lines of code that uses $. I am wondering if it's possible to make Angular's JQLite available outside of angular apps somehow? My first tries of simply seeing if JQLite was available did not work. Basically I would like either the variable $ or jQuery to be available anywhere (or at least within my ng-app area) to see if it contains enough functionality to let these jquery plugins/code work.
Here's the source https://github.com/angular/angular.js/blob/master/src/jqLite.js
Actually, you can, but it almost certainly won't help.
If you wanted to "export" it, you'd simply need to add this after the Angular JavaScript is loaded:
window.jQuery = window.$ = angular.element;
Now calling $() or jQuery() will run jqLite. Here's why it most likely won't work: jqLite doesn't support selectors, meaning you can't do
angular.element('.foo').html();
You'll just get an error stating that Selectors not implemented.
Here's a jsFiddle showing it working when used on elements directly, but failing with the selector.
Edit
Based on Yashua's suggestion below, here's an update using his trick to enable selectors. It's a nice trick/hack if jqLite is good enough.
Personally, I don't agree that you should just include jQuery. It's another large library to depend on, and if you are only performing a couple of one-liners, you should be able to integrate them into Angular directly using proper directives. I'd take the time to see if you can rewrite the existing code.

Defining a scope for JQuery variables, different js - single page

I have 2 js on a single page. In one of the js I am doing var jQuery = $.noConflict(true); and after that using all the jQuery methods using jQuery object, like jQuery("#div").hide();
In another js, I am using the traditional $ variable & accessing the jQuery methods as $("#div").hide();
When on a page I use either of the 2 js', things work fine. However, when I include both, the $ in the second js seems to be overwritten by jQuery. for example, in js 2 I can no longer do $("#div").hide() but if I use jQuery instead of $, it works fine - jQuery("#div").hide();!
Why am I experimenting this is because the first div will be distributed to different websites as a plugin, and the second js could be the Jquery written by that website developer.
Please help me figure out where am I going wrong..
Thanks.
You're using noConflict, which releases jQuery's hold on $. Doesn't matter if it's in separate scripts, since it's the same global environment.
In the script where you want to use $, define that variable inside your .ready() handler.
jQuery(function($) {
$('foo').bar();
});
The easiest way to solve this is to use a pattern that plugin authors employ.
(function($) {
// Use $ safely within the function
})(jQuery); // Passes in jQuery object
This is function is created and invoked immediately, and at the same time allows you to pass a variable into the scope of the function. Using this, you can carry on referring to $ in your code and not worry about overwriting it or conflicting.
The jQuery documentation advises this pattern
Files do not have scope, you dereferenced $ with noConflict for your entire page. Anything

Categories

Resources