dojo how to override dijit class method - javascript

I need to override onClick method in the Tree.js. Is there any common way to override dojo/dijit classes methods?

I'm a bit confused, since you were already doing this in the last question you posted.
You've got a few choices, depending on what you want to do.
Clobbering method stubs
In the case of true stubs like onClick, it's potentially as easy as clobbering that method on your widget instance.
Programmatically:
var myWidget = new dijit.Tree({
...,
onClick: function(item, node, evt) {
/* handler code here */
}
};
Or declaratively (this is exactly the same as what you were doing in your last question):
<div dojoType="dijit.Tree" ...>
<script type="dojo/method" event="onClick" args="item,node,evt">
/* handler code here */
</script>
</div>
Connecting to method invocations
In other cases, perhaps you need to do something whenever a given method gets called, in which case you could use the widget's connect method (which is a nicer version of dojo.connect that will automatically clear itself when the widget is destroyed). In that case you could do something like this:
Programmatically:
//execute the given function whenever myWidget's onClick method is called
myWidget.connect(myWidget, 'onClick', function(item, node, evt) {
/* handler code here */
});
Declaratively, this can be done very similarly to the above, except instead of type="dojo/method", make sure you use type="dojo/connect"
<div dojoType="dijit.Tree" ...>
<script type="dojo/connect" event="onClick" args="item,node,evt">
/* handler code here */
</script>
</div>
Note that when you connect like this, your code will execute after the method whose invocation you are connecting to. If you need to do something before, your best option is probably to dojo.declare your own extension to the widget. If you need to go that far, I'll elaborate, but I think you'll likely be set with one of the above options.
Edit #1: Connecting the dots (no pun intended...oh heck, yes it was)
Since it seems that my comment appended to my answer to the original question was somehow not clear enough, here's a code block modifying the original code in that question based on the simple two steps exactly as I explained in that comment. The only tiny wrinkle is that the arguments passed to _onClick are slightly different.
<div dojoType="dijit.Tree" ...>
<script type="dojo/connect" event="_onClick" args="node,evt">
/* handler code here. In this case, item is accessible via node.item */
</script>
</div>
This solution may feel a bit sub-optimal since it involves connecting to a method that's suggested to be private. However, it's a way that should work regardless of whether openOnClick is true or not. If you are certain you're going to have openOnClick set to true, you could potentially write a single function, then connect it to both onClick and onOpen instead (both get passed the item, then the node).
Edit #2: Common functions, connecting programmatically
To answer your follow-up questions, I'd like to actually address them in reverse order - since if you are interested in connecting programmatically, it will actually make the other question easier to answer.
So first, to answer your connect question: you definitely don't want to be using dojo.byId, as that's not giving you the Tree widget, that's giving you some DOM node (probably the topmost) associated with the widget. As a general rule, dojo methods don't know anything about dijit stuff.
What you do want to be doing, is what I suggested above. Here it is applied as per the code you attempted. Also note that onClick has a capital C - another general rule: widget events use camel case notation, as a way to distinguish them from plain DOM events which don't.
var mytree = dijit.byId("mytree");
mytree.connect(mytree, "onClick", function(item) {
/* ... */
});
Now, to take that a step further and resolve your other inquiry, if you want to bind some common functionality to onClick and onOpen and onClose, you could define a function first, then connect it to both. This is one of the many things that makes JavaScript awesome - the availability of functions as first-class objects that can be easily passed around.
function handleClick(item) {
/* do stuff here.
Inside this function you can assume 'this' is the tree,
since connect will ensure it runs in-context.
*/
}
var mytree = dijit.byId("mytree");
mytree.connect(mytree, "onClick", handleClick);
mytree.connect(mytree, "onOpen", handleClick);
mytree.connect(mytree, "onClose", handleClick);
Now there's one important remaining question: where should we do this? The best place is likely inside a function passed to dojo.ready (or dojo.addOnLoad, same thing, ready was added as a synonym in 1.4) so that it will run only after:
The DOM is parsed by the browser
All dojo.required modules have loaded
If you set parseOnLoad: true in djConfig, all widgets defined in the document's HTML will already be instantiated
So, sometime after your dojo.requires, in script, you'd do this:
dojo.ready(function() {
/* code from previous example goes here */
});
Give it a shot.
Also, if you're interested in a bit of reading, I've actually written about a couple of topics I touched on in this edit:
Of Dijits and DOM Nodes
dojo.required Reading

You could use:
dojo.connect(tree, 'onClick', function(item) {
/** Your Action **/
});

Related

Overwrite listener without handler reference, is it ok to access the (possibly private) events property on a Ext.data.store?

I'm working on replacing an Ext.data.Store load event handler.
The variable me is different every time within the code block but me.store is the same (obtained via StoreManager.lookup). I want the store event listener to update the various me references. Best way i could find was to add another listener (and delete the old one since i don't need it anymore)
I haven't been able to use un / removeListener i.e. it had not effect.
I've found that i could replace the it by accesing the me.store.events and popping the listener from the load event. However this feels hacky and it might make the code dependant on a specific ExtJS version (4.2) since i don't know if it's a private property or not.
Also me.store.hasListeners['load'] doesn't get notified so it only helps because it removes the actual listener but not in the intended manner. The docs don't mention it, but i'm wondering if it may be an inherited property which can be accessed freely.
Are there any alterntives to the working approach i've come to? Can i remove all event handlers for an event without having a reference to the handler? Or is there a simpler approach i'm missing?
var me = this; // an enriched Ext.form.FormPanel, different every time code runs
me.store //obtained via StoreManger.lookup - so the same every time
me.storeLoaded = function (store, records,successful, opts) {
// some code to select a record from records and use it
me.loadRecord(record);
}
};
if (!me.store.hasListener('load')) {
me.store.on('load', me.storeLoaded);
} else{
//tried this, but it doesn't remove it, probably because me.storeLoaded is different each time (parentForm is different)
me.store.un('load', me.storeLoaded);
//this feels hacky, i couldn't find out if events is a private property
if (me.store.events && me.store.events['load']){
me.store.events['load'].listeners.pop()
}
me.store.on('load', me.storeLoaded);
}
The easiest way to implement adding/removing listeners is using the destroyable parameter as described in the addListener function. That way, you can always be sure which one is removed.
Example:
setActive:function(cmp) {
cmp.myActiveListeners = cmp.eventStore.on({
destroyable: true,
load:cmp.refreshStores,
filterchange:cmp.refreshStores,
scope:cmp
});
},
setInactive:function(cmp) {
Ext.destroy(cmp.myActiveListeners);
},
I cannot recommend to blindly remove ALL listeners, since they may be added by other components (e.g. combobox) that you add later. To track down these bugs will grow you quite some gray hairs.
I was able to find an answer in this article ExtJS overwrite listener:
Sometimes you need to overwrite an event listener in ExtJS. Usually
listeners are registered like this myStore.on('load',
this.myFunction, this); then to remove our previously registered
listener, all we have to do is call un (which is an alias for
removeListener): myStore.un('load', this.myFunction, this);
But, what happens when you don't know what function is registered?
Sometimes you will not have a reference to the original function that
was registered. This situation may arise if there is code that exists
in a different flow or may even come as a package! If that is true,
the you may not be able to get a reference to the javascript function
or edit the existing code. In this case, we will have to look at all
of the functions that are registered for this event. We can then
remove the listeners just for a certain event by calling
clearListeners.
clearListeners was the method i was looking for.
It would seem he uses the events property so i assume it is a valid use. It could be translated in my case to:
me.store.events.load.clearListeners()
However since i will only be using the load event on this particular store, i will simply call on them all.
me.store.clearListeners()
Thanks to Alexander, by suggesting not to remove all listeners that actually helped me find the article. However i will stil go with his solution, even if it polutes the store object because i like it better than clearing all listeners on a store, even if only for a specific event.

How to re-evaluate a script that doesn't expose any global in a declarative-style component

I have been writing a reusable script, let's call it a plugin although it's not jQuery, that can be initialised in a declarative way from the HTML. I have extremely simplified it to explain my question so let's say that if a user inserts a tag like:
<span data-color="green"></span>
the script will fire because the attribute data-color is found, changing the color accordingly.
This approach proved very handy because it avoids anyone using the plugin having to initialise it imperatively in their own scripts with something like:
var elem = document.getElementsByTagName('span')[0];
myPlugin.init(elem);
Moreover by going the declarative way I could get away without defining any global (in this case myPlugin), which seemed to be a nice side effect.
I simplified this situation in an example fiddle here, and as you can see a user can avoid writing any js, leaving the configuration to the HTML.
Current situation
The plugin is wrapped in a closure like so:
;(function(){
var changeColor = {
init : function(elem){
var bg = elem.getAttribute('data-color');
elem.style.background = bg;
}
};
// the plugin itslef looks for appropriate HTML elements
var elem = document.querySelectorAll('[data-color]')[0];
// it inits itself as soon as it is evaluated at page load
changeColor.init(elem);
})();
The page loads and the span gets the correct colour, so everything is fine.
The problem
What has come up lately, though, is the need to let the user re-evaluate/re-init the plugin when he needs to.
Let's say that in the first example the HTML is changed dynamically after the page is loaded, becoming:
<span data-color="purple"></span>
With the first fiddle there's no way to re-init the plugin, so I am now testing some solutions.
Possible solutions
Exposing a global
The most obvious is exposing a global. If we go this route the fiddle becomes
http://jsfiddle.net/gleezer/089om9z5/4/
where the only real difference is removing the selection of the element, leaving it to the user:
// we remove this line
// var elem = document.querySelectorAll('[data-color]')[0];
and adding something like (again, i am simplifying for the sake of the question):
window.changeColor = changeColor;
to the above code in order to expose the init method to be called from anywhere.
Although this works I am not satisfied with it. I am really looking for an alternative solution, as I don't want to lose the ease of use of the original approach and I don't want to force anyone using the script adding a new global to their projects.
Events
One solution I have found is leveraging events. By putting something like this in the plugin body:
elem.addEventListener('init', function() {
changeColor.init(elem);
}, false);
anybody will be able to just create an event an fire it accordingly. An example in this case:
var event = new CustomEvent('init', {});
span.dispatchEvent(event);
This would re-init the plugin whenever needed. A working fiddle is to be found here:
http://jsfiddle.net/gleezer/tgztjdzL/1/
The question (finally)
My question is: is there a cleaner/better way of handling this?
How can i let people using this plugin without the need of a global or having to initialise the script themselves the first time? Is event the best way or am I missing some more obvious/better solutions?
You can override Element.setAttribute to trigger your plugin:
var oldSetAttribute = Element.prototype.setAttribute;
Element.prototype.setAttribute = function(name, value) {
oldSetAttribute.call(this, name, value);
if (name === 'data-color') {
changeColor.init(this);
}
}
Pros:
User does not have to explicitly re-initialize the plugin. It will happen automatically when required.
Cons:
This will, of course, only work if the user changes data-color attributes using setAttribute, and not if they create new DOM elements using innerHTML or via some other approach.
Modifying host object prototypes is considered bad practice by many, and for good reasons. Use at your own risk.

Basic JQuery syntax: What mechnaic is at work in this small (2 line) piece of JavaScript / JQuery

So here' s the piece of code. I'm very new to JavaScript so don't be afraid to explain the obvious
$(".my-css-class").on("click", function() {
($(this).attr("data-property-1"), $(this).attr("data-property-2"), this);
});
There's an element in the .jsp page that looks like this:
<i class="clickMe"></i>
I know the .jsp creates a link-icon, and that the above JavaScript is an event handler. I know that it passes these 3 values as arguments another JavaScript method:
function doStuff(prop1, prop2, obj) {
if (prop1 == 'foo') {
//do stuff with prop2
}
else{
// do stuff with obj
}
}
It all works fine. What I want to know is what exactly is going on to make it work? I can't find anything in the code that connects what the event-handler returns to the 'doStuff' java-script function.
The names are totally different, so it's not reflection, it can't be parameter matching because there's other functions with the same number and type of parameters in the file, it can't be convention based because it still works if I find/replace the name of the function to gibberish.
I guess basically I'm asking what this line is doing:
($(this).attr("data-property-1"), $(this).attr("data-property-2"), this);
tl;dr: I'm at a loss, I know how the properties get as far as the onClick event-handler's anonymous function - but how does JavaScript know to pass them as arguments the to the doStuff() function?
the onClick event is a standard event triggered on click of any clickable html element and is automatically raised by the DOM.
You are hooking in to this by listening on any matched ".my-css-class" elements for an onClick Event.
The jquery syntax ".on" has been simplified over time and allows you to hook into any number of events like "submit" - OnSubmit event , or "load" - onLoad Event
Wherever your on("click", myFunction) event hook is picked up, your myFunction will execute.
Looking at your second point...
because it still works if I find/replace the name of the function to gibberish.
The DoStuff function will be found and replaced across all files in your site? or page? or open tabs? , so therefore it must exist somewhere as "doStuff(" or "giberish(".
so when you do a global find/replace, do each one slowly, until you locate it.
Finally, when you do a view source in the browser, this should either explicitly show you the doStuff function, or at the very least give you a clue as to satelite files loaded at runtime, where you can go and investigate.
Use firebug in firefox to debug loaded resources; the ".net tab" to view external loaded resources and the html/javascript they might contain. (for example: your master page might be loading in an embeded resource that contains the doStuff method, becuase of a user or server control reference in that master page)
Also have a look at this:
http://www.developerfusion.com/article/139949/debugging-javascript-with-firebug/
You can step through the javascipt piece by peice until it hits the doStuff method.
Just remember to set at least 1 breakpoint ;-)

How to use dojo/behavior on dojo widget?

I read this article (Using dojo.behavior), and want to use the behavior module in my project as event handling module.
But I have a problem that, for DOM nodes, it works wonderful, but how can I use it on the Dojo widgets?
If I use dojo/on module, I can do it like this:
var buttonNode = dijit.byId("myButton");
on(buttonNode, "onClick", buttonHandler);
or
dijit.byId("myButton").onClick = buttonHandler;
But, if I use behavior module,
behavior.add({
"#myButton": {
onClick: buttonHandler
}
});
it doesn't work. (Of course I called behavior.apply() after I finished page render.)
The code below doesn't work either.
behavior.add({
"[widgetid='myButton']": {
onClick: buttonHandler
}
});
After some investigation, I found the reason the code above not work is because a button widget is composed by many s and an inner . And if I use the id specified by data-dojo-id, it will point to a instead of the that I hope the event bind with.
I found a solution which can walk out this situation,
behavior.add({
"[widgetid='myButton'] input": {
onclick: buttonHandler
}
}
but the css selector is too complex and it depends on what type the widget is.
Is there a good solution to apply dojo/behavior on widgets just like on dom nodes?
It looks like what you really what is to hook up an event on the widget object, but behavior is designed to access the DOM instead. So, I think you're stuck with your workaround.

Watch for a property creation event?

I need to be able to determine when an object is created (not a DOM element -- a JavaScript object).
An answer to this question has some very useful looking code for creating observable properties, so you can have a function fire when a property changes.
In my situation I need to do something when the object/property is created, not an existing property changed, and my limited understanding of such matters did not help me figure out if or how I could use that code to do this after much squinting.
The situation is: page loads a bunch of scripts. Some of the scripts create things that are needed by other scripts, e.g:
ThisStuff = (function () {
// blah blah
return self;
} ());
Some other code needs to initialize this ThisStuff, whenever it's available, which may be after the DOM is done loading. The user doesn't actually need ThisStuff right away, so it's fine for it to happen whenever the script is done loading. So I would like to do something along lines of:
$(document).ready(function() {
wheneverIsAvailable(window,'ThisStuff', function(object) {
object.init(args);
})
});
I realize there are other solutions to this problem (changing script order, or loading scripts on demand) but those are difficult because of the architecture. So I'm only interested in a way to do this versus other solutions. If jQuery offers some such functionality, that's fine also as I'm using it.
You could have a setInterval checking a number of times a second to watch the specific variable. You can check whether it is created using obj.hasOwnProperty(prop). When it is created, you invoke the function, and clear the interval.
It might be dirty but it might also just work fine for you.
Edit: I coded this for you: http://jsfiddle.net/jhXJ2/2/. It also supports passing additional arguments to the function.
window.__intervals = [];
function wheneverIsAvailable(obj, prop, func) {
var id = (Math.random()+"").substring(2);
var args = arguments;
window.__intervals[id] = window.setInterval(function() {
if(obj.hasOwnProperty(prop)) {
window.clearInterval(window.__intervals[id]);
func(Array.prototype.slice.call(args, 3));
// Call function with additional parameters passed
// after func (from index 3 and on)
}
}, 1000/ 50);
}
wheneverIsAvailable(window, 'test', function() {
alert(arguments[0]);
}, 'Woot!');
window.setTimeout('window.test = 123', 1000);
This is a bit far-fetched but it might work.
You would need to use knockoutjs, a javascript library. It's awesome but is built for a slightly different purpose.
Anyways it has a dependentObservable thing which allows to fire up an event whenever a certain value changes. Now I know you want on creation but you can check whether your variable holds any value (other than what you provided initially), if yes then consider it initialize.
Let me know if you think this sounds feasible.

Categories