Please explain the use of Ember.$? - javascript

Please could someone explain why I see examples of people using Ember.$ for jQuery rather than $.

It's just jQuery attached to Ember's namespace. Same as jQuery. Maybe someday if Ember was feeling like removing the dependency on jQuery they could implement some of the functionality, but I remember Tom and Yehuda saying there was no point in re-implementing jQuery, it already does it's job really well.
Here's the code that does it:
init: function() {
if (!this.$) { this.$ = jQuery; }

There is 2 $ in ember and they both link to jQuery internally.
There is a global one Ember.$. The goal is the be able to replace jquery if needed in the future.
There is also one for each view instance (this.$()) which points to the jquery element used by the view:
...
didInsertElement: function(){
alert(this.$().position().top);
}
...
This is equivalent of doing Em.$(this.element). If you pass a selector, it will scope the selection to the view element only so this.$(".selector") is the same as Em.$(this.element, ".selector").

Related

Custom Unobtrusive Validation Method Not Firing as Per Documentation

I've been attempting to implement a ASP.NET MVC custom validation method. Tutorials I've used such as codeproject explain that you add data-val-customname to the element. Then jQuery.validate.unobtrusive.js then uses the third segment of the attribute
data-val-<customname>
as the name of the rule, as shown below.
$.validator.addMethod('customname', function(value, element, param) {
//... return true or false
});
However I just can't get the customname method to fire. By playing around I have been able to get the below code to work, but according to all the sources I've read Unobtrusive validation should not work like this.
$.validator.addMethod('data-val-customname', function(value, element, param) {
//... return true or false
});
I've posted an example of both methods
jsfiddle example
Any help would be much appreciated
I've updated my question hopefully to make clearer.
I have finally found got there in the end, but still feels like too much hard work and therefore I've probably got something wrong. Initial I was scuppered by a bug in Chrome Canary 62 which refused to allow the adding of a custom method.
My next issue was having to load jQuery, jQuery.validate and jQuery.validate.unobtrusive in the markup and then isolate javascript implementation in a ES6 class. I didn't want to add my adaptors before $().ready() because of my class structure and loading of the app file independent of jQuery. So I had to force $.validator.unobtrusive.parse(document);.
Despite this I was still having issues and finally debugged the source code and found that an existing validator information that is attached to the form was not merging with the updated parsed rules, and essentially ignoring any new adaptors added.
My final work around and admit feels like I've done too much, was to destroy the initial validation information before my forced re-parse.
Here is the working jsfiddle demo
Here is some simplified code
onJQueryReady() {
let formValidator = $.data(document.querySelector('form'), "validator" );
formValidator.destroy();
$.validator.unobtrusive.adapters.add("telephone", [], function (options) {
options.rules['telephone'] = {};
options.messages['telephone'] = options.message;
});
$.validator.unobtrusive.parse(document);
$.validator.addMethod("telephone", this.handleValidateTelephoneNumber);
}

How to deal with DOM elements?

I am learning about writing custom JavaScript for my Odoo 10 addons.
I've written the following piece of code:
odoo.define('ioio.io', function(require) {
'use strict'
const e = $('div.o_sub_menu_footer')
console.log('--testing--'.repeat(7))
console.log(e)
// the "Powered by Odoo" down the secondary menu
e.remove()
})
The code is well loaded and I can see my testing string in the console.
However when this code is being loaded before the target div, so e empty/not yet filled and thus its content is not removed.
Doing it manually from the console works.
My question is what is the right way to do that? And how to know exactly when the code gets executed?
You can
put your html code before the script tag in your file
use jQuery $(document).ready(...);
Place your script at the bottom of the <body> tag to make sure the DOM renders before trying to manipulate it.
This is an Odoo specific question, so you should use the Odoo standard way, which is via its base JS class. That class contains a ready() method which does exactly what you need.
In your case, to use that function, you need to require the class first. Then you can use ready().
Updating your code, it should look like this:
odoo.define('ioio.io', function(require) {
'use strict'
// require base class
var base = require('web_editor.base');
//use its ready method
base.ready().done(function () {
// put all the code you want to get loaded
// once the DOM is loaded within this block
const e = $('div.o_sub_menu_footer')
console.log('--testing--'.repeat(7))
console.log(e)
// the "Powered by Odoo" down the secondary menu
e.remove()
});
})
While your accepted answer leads to the same outcome, you might want to update it to this one since this is the Odoo way. It's generally advised to work within the Odoo framework as much as possible and customise only if really needed. (Though it can be tough to learn what features Odoo already provides because of its poor documentation.)

What kind of pattern is this?

I've learnt development by looking at other people's codes, so I'm not very good with terminologies. Lately I've been writting my JS/Jquery this way:
$(document).ready(function() {
testingFunc.init();
});
var testingFunc = {
$object: $('#object'),
init: function() {
var _that = this;
console.log($object);
}
}
Can someone please tell me if this a pattern of some sort? Or can someone please tell me how to describe the code I've done above?
This particular style represented in your code is an "object literal" pattern. It differs only slightly from a "module" pattern when you find yourself not requiring specific properties or methods to be private.
Before getting into a trap of terminologies, you may want to understand (in principle) what Javascript patterns are, and then identify those which may be architecturally best-fit for your project.
You may get an in-depth understanding from this mini-book from Addy Osmani:
http://addyosmani.com/resources/essentialjsdesignpatterns/book/
And a high-level article from him:
http://addyosmani.com/largescalejavascript/
The first part is using a jQuery selector with the listener "ready". What this means is that the callback function attached to the selector and listener will run once the document (in this case the browser window) is ready (in web terms, this means when the page finishes loading).
The second part of your code is following a standard called object literal, which is a JavaScript methodology that follows the principles of key->value
Perhaps you can name it the Object Literal pattern like used by Rebecca Murphey in her article. However I do not think that it's widely adopted as an official name for this kind of code structure, but it seems appropriate.
I guess you are wondering about the ready function. In order to understand how it works, you have to know that when you load an HTML page into you browser, the HTML structure is turned into a javascript tree called "DOM" (Document Object Model). In your sample, the DOM is referenced through the variable named document. To populate this tree, each markup has to be initialized as a javascript object. Once this job is done, the "ready" event is raised, invoking every function which is bound to it. To summarize :
$(document).ready(function () { testingFunc.init(); });
// translation : Once the DOM has been initialized, call "init".
Regarding your code, $('#object') attempts to query the DOM tree to find a node with an id set to "object" (e.g. <div id="object">). However, the document is probably not yet fully initialized. As a result, this query might fail. To avoid this risk you should rather do this :
var testingFunc = {
$object: null,
init: function() {
this.$object = $('#object');
console.log(this.$object);
}
}
You can think of the DOM as a folder structure, where each folder and file is an HTML markup. jQuery browses the DOM tree the same way that you browse your files explorer.

Add function to existing JQuery plugin

Is it possible to add a function to a plugin without modifying the actual plugin? Can I do something like this in my site's js file?
$.fn.Watermark.Refresh = function() {
$.Watermark.HideAll();
$.Watermark.ShowAll();
}
or
(function($){
$.fn.Watermark.Refresh = function() {
$.Watermark.HideAll();
$.Watermark.ShowAll();
};
})(jQuery);
neither worked, the first says $ is undefined, the second that jQuery is undefined...
ideas?
Solution: Either method works, just include the jquery file before the site js file.
You can add those functions if you want to, but you'll have to make sure that you're also loading jQuery itself and the plugin to be modified. If you're getting those errors (that jQuery or "$" are not defined), then you have not correctly done that.
Now, though it's true that you can add those functions, I have to wonder what the point would be. If I were to do this, for example:
$.fn.css.myFunction = function() { return "hello world"; };
then it would be possible to call it:
var str = $.fn.css.myFunction();
but so what? What good does that do me? I don't think it's very useful.
Make sure you are including the plugin after jQuery.

How to isolate different javascript libraries on the same page?

Suppose we need to embed a widget in third party page. This widget might use jquery for instance so widget carries a jquery library with itself.
Suppose third party page also uses jquery but a different version.
How to prevent clash between them when embedding widgets? jquery.noConflict is not an option because it's required to call this method for the first jquery library which is loaded in the page and this means that third party website should call it. The idea is that third party site should not amend or do anything aside putting tag with a src to the widget in order to use it.
Also this is not the problem with jquery in particular - google closure library (even compiled) might be taken as an example.
What solutions are exist to isolate different javascript libraries aside from obvious iframe?
Maybe loading javascript as string and then eval (by using Function('code to eval'), not the eval('code to eval')) it in anonymous function might do the trick?
Actually, I think jQuery.noConflict is precisely what you want to use. If I understand its implementation correctly, your code should look like this:
(function () {
var my$;
// your copy of the minified jQuery source
my$ = jQuery.noConflict(true);
// your widget code, which should use my$ instead of $
}());
The call to noConflict will restore the global jQuery and $ objects to their former values.
Function(...) makes an eval inside your function, it isn't any better.
Why not use the iframe they provide a default sandboxing for third party content.
And for friendly ones you can share text data, between them and your page, using parent.postMessage for modern browser or the window.name hack for the olders.
I built a library to solve this very problem. I am not sure if it will help you of course, because the code still has to be aware of the problem and use the library in the first place, so it will help only if you are able to change your code to use the library.
The library in question is called Packages JS and can be downloaded and used for free as it is Open Source under a Creative Commons license.
It basically works by packaging code inside functions. From those functions you export those objects you want to expose to other packages. In the consumer packages you import these objects into your local namespace. It doesn't matter if someone else or indeed even you yourself use the same name multiple times because you can resolve the ambiguity.
Here is an example:
(file example/greeting.js)
Package("example.greeting", function() {
// Create a function hello...
function hello() {
return "Hello world!";
};
// ...then export it for use by other packages
Export(hello);
// You need to supply a name for anonymous functions...
Export("goodbye", function() {
return "Goodbye cruel world!";
});
});
(file example/ambiguity.js)
Package("example.ambiguity", function() {
// functions hello and goodbye are also in example.greeting, making it ambiguous which
// one is intended when using the unqualified name.
function hello() {
return "Hello ambiguity!";
};
function goodbye() {
return "Goodbye ambiguity!";
};
// export for use by other packages
Export(hello);
Export(goodbye);
});
(file example/ambiguitytest.js)
Package("example.ambiguitytest", ["example.ambiguity", "example.greeting"], function(hello, log) {
// Which hello did we get? The one from example.ambiguity or from example.greeting?
log().info(hello());
// We will get the first one found, so the one from example.ambiguity in this case.
// Use fully qualified names to resolve any ambiguities.
var goodbye1 = Import("example.greeting.goodbye");
var goodbye2 = Import("example.ambiguity.goodbye");
log().info(goodbye1());
log().info(goodbye2());
});
example/ambiguitytest.js uses two libraries that both export a function goodbye, but it can explicitly import the correct ones and assign them to local aliases to disambiguate between them.
To use jQuery in this way would mean 'packaging' jQuery by wrapping it's code in a call to Package and Exporting the objects that it now exposes to the global scope. It means changing the library a bit which may not be what you want but alas there is no way around that that I can see without resorting to iframes.
I am planning on including 'packaged' versions of popular libraries along in the download and jQuery is definitely on the list, but at the moment I only have a packaged version of Sizzle, jQuery's selector engine.
Instead of looking for methods like no conflict, you can very well call full URL of the Google API on jQuery so that it can work in the application.
<script src="myjquery.min.js"></script>
<script>window.myjQuery = window.jQuery.noConflict();</script>
...
<script src='...'></script> //another widget using an old versioned jquery
<script>
(function($){
//...
//now you can access your own jquery here, without conflict
})(window.myjQuery);
delete window.myjQuery;
</script>
Most important points:
call jQuery.noConflict() method IMMEDIATELY AFTER your own jquery and related plugins tags
store the result jquery to a global variable, with a name that has little chance to conflict or confuse
load your widget using the old versioned jquery;
followed up is your logic codes. using a closure to obtain a private $ for convience. The private $ will not conflict with other jquerys.
You'd better not forget to delete the global temp var.

Categories

Resources