i have the following form item
{
fieldLabel:'Username'
,id:"username"
,name:'username'
,allowBlank:false
,plugins:[Ext.ux.plugins.RemoteValidator]
,rvOptions: {
url:'/registration/member/valid-username'
}
is it possible to remove plugins later via code?
I don't think so. init in the plugin runs when the component is initialized, so "later in the code" it's too late - "the damage has been done", and the plugin has hooked into the component's events, etc. It would be cool if I were wrong.
Well, it's functionally possible to support plugin deactivation (not sure about actually removing the plugin altogether), but most plugins probably don't do so unless they have some reason to support it. You should be able to write an override to the plugin and insert code that would allow you to activate/deactivate its functionality. Depends on the specific plugin of course, but if the plugin is well-written it should be overrideable.
My general approach would be something like:
Ext.override(Ext.ux.plugins.SomePlugin, {
isActive: true,
doSomething: function(){
if(this.isActive){
// copy orig doSomething
}
}
});
Then you could simply set pluginInstance.isActive = true/false as needed. Note that this is simplistic -- your plugin might take a lot more work to override effectively. But this approach would be a good place to start.
Or you could maybe get fancy and use createInterceptor functions on the plugin to do something similar without duplicating code.
Related
I have created one application. In the application I have used javascript confirm function a lot.
confirm("Do you want to proceed");
I don't like the default UI of confirm. I want to use customized confirm with better UI.
Problem:
I got some options for customized confirm. But if I will use them I need to change all default confirm methods(needs lot of changes).
Is there any way to achieve this in minimal change.
like:
window.confrim = function() {
/*
What logic I should write which will return the
value(true or false) selected by user.
*/
}
I have one JS file which is imported in all HTML files.
So I can place the above function logic in the common JS file.
The biggest issue with customising confirm is that the native confirm is blocking. So you can just write:
if( confirm("Continue?")) {
doStuff();
}
But your own code can't do that. Instead, you would need to create some kind of callback. An example might be:
myConfirm("Continue?",function() {
doStuff();
},function() {
cancelStuff();
});
Exactly how you implement this is up to you - I actually have a more flexible version of this on my projects, where I make a call of the form:
Dialog("Title", "Contents", [array of buttons]);
// [array of buttons] being an array of objects like:
{
"text": "Button text",
"action": function() {doSomething();},
"optional_extras": "more stuff"
}
The cool thing about writing your own stuff is that you can extend it freely to suit your needs as the project grows.
Confirm box is part of the browser not the DOM. So, its not possible to modify that. You can try custom confirm boxes like http://jqueryui.com/dialog/#modal-confirmation OR
http://onehackoranother.com/projects/jquery/boxy/
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.
I've noticed that RequireJS creates script tags in the tag as it loads modules.
Is there anyway to configure RequireJS to "tag" those elements w/ a class or an attribute of some kind that I could later target w/ jQuery later on?
e.g.:
var $requireJsScripts = $('script.require-script');
--UPDATE--
Ok.. I think I can get by on this little workaround for now. Thanks to this answer for the breadcrumb on require.s.contexts._.defined. I'd still like to hear if anyone knows of a way to configure RequireJS to do something similar to what was laid out in the original question...
var loadedRjsModules = Object.keys(require.s.contexts._.defined);
var $scripts = $('script');
$scripts.each(function () {
if ($(this).data('requiremodule') && $.inArray($(this).data('requiremodule'), loadedRjsModules)) {
console.log(this);
}
});
Looking at the source code, I don't see how RequireJS would allow adding anything custom to the script nodes at creation. The routine that creates them has no provision for it. The code that fleshes them out upon creation does not support it either.
There's an onResourceLoad hook considered part of the internal API. It could be used with the code you've put in your question instead of relying on require.s.contexts._.defined, which as far as I know is fully private and subject to change without notice.
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.
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 **/
});