I came across this problem while I was trying to customize one of the 3rd party plugins we use in our web application.
We tend to not update the core plugin file just to make sure we don't run into issues when we upgrade 3rd party plugins. Best way to avoid this is either extend existing functionality with new set of methods or to override existing plugin methods outside the main plugin file.
One of the plugin I tried to apply this approach was written in an unusual way where it defined all methods in an object outside main plugin function.
So my question is how do we Extend or more likely Overload one of the methods (validateFields, showErrorMsg) of valEngine plugin without updating plugin code.
Here is a JS Fiddle with a rough prototype.
http://jsfiddle.net/g9Ng9/
// Can't update any of this.
(function($) {
"use strict";
var methods = {
validateFields: function() {
console.log('I perform validation of fields!');
},
showErrorMsg: function() {
console.log('I log error messages');
}
};
$.fn.valEngine = function() {
console.log('I access methods within method object!');
methods.validateFields();
methods.showErrorMsg();
};
})(jQuery);
// Can't update any of this.
// Your code goes here
Thanks in advance.
There is no direct way to get this done as the whole point of keeping those methods outside plugin definition is to keep them private.
So in order to overload the methods object method like 'validateFields' we need to amend the core plugin file in some form.
Something similar to what Abraham Uribe mentioned here in his JS Fiddle: http://jsfiddle.net/g9Ng9/1/
// Can't update any of this.
(function($) {
"use strict";
var methods = {
validateFields: function() {
console.log('I perform validation of fields!');
},
showErrorMsg: function() {
console.log('I log error messages');
}
};
$.fn.valEngine = function() {
console.log('I access methods within method object!');
methods.validateFields();
methods.showErrorMsg();
};
$.fn.valEngine.methods=methods;
});
$().valEngine();
$.fn.valEngine.methods.validateFields=function(){
console.log("validateFields");
};
$().valEngine();
You can extend or overload jQuery methods by using extend() method.
A simple example would be
jQuery.fn.extend({
validateFields: function() {
// your custom code here
}
});
I am not sure but, the namespace in your case would be jQuery.fn.ValEngine.
Related
I have a question regarding the structure of a jQuery plugin that I found.
For better understanding, here is a simplified example of the plugins structure:
// Regular constructor function
function MyPlugin() {
this.myValue = "My Value";
}
// Methods on the prototype
MyPlugin.prototype.showValue = function() {
alert($.myplug.getValue());
}
MyPlugin.prototype.getValue = function() {
return this.myValue;
}
// jQuery plugin
$.fn.myplug = function() {
// Why is is possible to access $.myplug here although it's not created yet?
return this.each(function() {
$(this).html($.myplug.getValue());
});
};
// Create new MyPlug instance
$.myplug = new MyPlugin();
// Calling the jQuery plugin on a DOM element
$('div').myplug();
For the most part, I get what is happening. The actual plugin logic seems to be written as a normal JavaScript "class".
This is followed by a jQuery plugin definition – I think, actually, some new method is added to jQuery's prototype. This is where things get tricky to me:
How is is possible to access the class instance inside the plugin, although the class is instantiated after the plugin definition? Is there a mechanism at work similar to variable hoisting?
In case you want to try something, here is a Fiddle of the example: http://jsfiddle.net/kq8ykkga/
$(this).html($.myplug.getValue()); isn't evaluated until you call $('selector').myplug(), executing the function body.
My web application is using a number of JQuery plugins such as the Datatables, and it's corresponding Row Reordering and Sorting plugins.
The problem is that we need to add some fixes to account for annoying IE8 inconsistencies.
So, for example, in one of the plugins _mouseDrag() method we need to add some extra logic at the start. Is there a way to override this function without modifying the plugin's source code?
In an OO language you would typically override a class method, add some logic and then call the super() method to avoid modifying an API's source code.
Therefore, I am wondering how we can best support this in JavaScript without modifying the library itself.
Thanks
Updated Question
Based on the example below, how do I override my _mouseDrag function which exists as the following in the 3rd party library:
(function( $, undefined ) {
$.widget("ui.sortable", $.ui.mouse, {
_mouseDrag: function (event){
...
}
How do I override this exactly?
Try this (see Demo: http://jsfiddle.net/2rZN7/):
(function ($) {
var oldPluginFunction = $.fn.pluginFunction;
$.fn.pluginFunction = function () {
// your code
return oldPluginFunction.call(this);
};
})(jQuery);
From Demo:
HTML:
<span id="text1">text1</span>
<span id="text2">text2</span>
JavaScript:
// define and use plugin
$.fn.pluginFunction = function (color) {
return this.each(function () {
$(this).css('color', color);
});
};
$('#text1').pluginFunction('red');
// redefine and use plugin
(function ($) {
var oldPluginFunction = $.fn.pluginFunction;
$.fn.pluginFunction = function () {
$(this).css('font-size', 24)
return oldPluginFunction.apply(this, arguments);
};
})(jQuery);
$('#text2').pluginFunction('red');
You can override plugins method by prototype it in a separate file without modifying original source file as below::
(function ($) {
$.ui.draggable.prototype._mouseDrag = function(event, noPropagation) {
// Your Code
},
$.ui.resizable.prototype._mouseDrag = function(event) {
// Your code
}
}(jQuery));
Now put your logic here or original code with your new idea that is needed in your project.
I am currently developing a rather complex jQuery plugin. One that I am designing to be extensible. The quandary I have is how to exactly provide my users with the APIs available to them.
There are two methods that I can come up with:
Provide the API via an object in the global scope
This is the method I am currently using. I do it similar to this:
(function ($, win, undefined) {
//main plugin functionality
function pluginStuff() { /*...including method calling logic...*/ }
//register function with jQuery
$.fn.extend({ Plugin: pluginStuff });
//register global API variable
win.PluginAPI = { extendMe: {}, getVar: function() {} };
})(jQuery, window);
Unfortunately since I impliment the standard $().plugin('method') architecture its a little strange to have to use the jQuery method for some things and the API variable for others.
Provide the API via an object placed in jQuery
I toyed with this method as well but its best practice to take up only a single slot in jQueries fn scope, as not to crowd the jQuery variable. In this method I would put my api variable in $.fn instead of the window:
//register function with jQuery
$.fn.extend({ Plugin: pluginStuff });
//register global API variable
$.fn.PluginAPI = { extendMe: {}, getVar: function() {} };
I would rather not break this convention and take up two places.
Now that I write this I can see a third option where I assign my plugins slot in jQuery's fn scope to be an object:
$.fn.Plugin = { plugin: pluginStuff, api: { extendMe: {}, getVar: function() {} } };
but how well received would this be if users had to do $('#elm').Plugin.plugin({ setting: 'value' }) to create a new instance of the plugin?
Any help or pointers would be greatly appreciated.
Please Note: I'm am not looking for a way to incorporate the API object into my plugin functionality. I am looking for a way to keep it separately modularized, but intuitively available for use/extension.
You could always do like
var plugin = function plugin() { /* do the main stuff */ };
// api stuff here
plugin.getVar = function() { };
plugin.extendMe = {};
$.fn.Plugin = plugin;
Or stick the extra stuff in an object that you assign to plugin.api.
Any way you do it, though, you're going to have to worry a bit about settings bleeding into each other. Since everything's going to be using the same function, regardless of how you choose to set it up, you'll need a way to keep invocations of the plugin separate from one another. Perhaps using something like, say, this.selector (in your plugin function) as a key into an associative array of properties, for example. I'd normally recommend .data() to attach settings to individual elements, but that doesn't help much if the same element gets the plugin called for it twice.
The method I eventually decided to use was registering the plugin under the fn namespace and the api variable under the jQuery $ namespace. Since methods and options set operate on an instance of the plugin $.fn is the best choice.
However, the API is global and does not link to a single instance. In this case $.fn doesn't quite fit. What I ended up using was something similar to this:
(function ($, win, undefined) {
//main plugin functionality
function pluginStuff() { /*...including method calling logic...*/ }
//register function with jQuery
$.fn.Plugin = pluginStuff;
//register global API variable
$.Plugin = { extendMe: {}, getVar: function() {} };
})(jQuery, window);
now you can create an use a plugin object as expected:
$('#elm').Plugin();
$('#elm').Plugin('option', 'something', 'value');
$('#elm').Plugin('method');
and you can easily extend and access the API:
$.extend($.Plugin.extendMe, {
moreStuff: {}
});
$.Plugin.getVar('var');
Thanks for the help everyone!
Could you please tell how to add a new method to Ext Core:
Ext.get('id').myMethod();
It doesn't work:
Ext.extend(Ext.Element, {
myMethod: function() {
this.on('click', function() {
alert(this);
});
}
});
(Assuming you are using ExtJS 3)
No, that's not the right way. We use extend when we need to extend a Class and create our own class.
Then if you would like to add additional functionalities or override some of the core methods, use Ext.override. This action is permanent and destructive since you can override any defined methods. We use override when we want to apply personal fixes or global changes (like overriding Ext.form.Field so to provide global, usable and cross fields functionalities).
For your case, it seems like you want to apply this method to all the elements you have captured from Ext.get. You will just need to override Ext.Element.
Ext.override(Ext.Element, {
myMethod: function() {
this.on('click', function() {
alert(this);
});
}
});
An working example is given here: jsfiddle.
Note that Ext.get is giving you Ext.Element on a successful call. If you would like to create a customized Ext.Element, be sure to modify Ext.get so it will return your own customized Ext.Element, or provides your own customized function to get elements.
Cheers!
I'm working on a jQuery plugin, following the pattern detailed in the Authoring guide. Basically:
(function($) {
// Private
var doSomething = function($element, settings) { ... }
var doSomethingElse = function($element, settings) { ... }
// Public
var methods = {
init: function(options) { ... },
show: function() { ... },
hide: function() { ... },
update: function(content) { ... }
};
$.fn.myPlugin = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || ! method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.myPlugin');
}
};
})(jQuery);
Here's my dislike: I have to pass the same "instance" variables to all of the private functions. I'm still working on becoming a JS pro — so pardon my incorrect term usage — but if I were doing this same thing in Ruby or PHP, I'd create a class with all of these public and private members and methods, and each instance of the class would correspond to an $element. Then I could do something like this (in JS):
var firstElement = new MyPlugin($element, settings);
firstElement.doSomething();
Rather than passing $element and settings to doSomething(), it already has access to those via this.$element and this.settings.
Get where I'm going with this? I'm looking for a more object-oriented approach, I guess. Now, I totally understand that JS doesn't have classes like Ruby or PHP. But between constructor functions, the module pattern, and regular object notation (like methods above), I'm not sure which is the best option for a jQuery plugin.
Can someone help point me in the right direction? Maybe some examples of existing jQuery plugins that do this well? Thanks!
The jQuery UI Widget Factory might be a good solution. It's useful for creating any kind of stateful jQuery plugins and can be used entirely separate from the rest of the jQuery UI suit.
Some useful links:
http://bililite.com/blog/understanding-jquery-ui-widgets-a-tutorial/
http://wiki.jqueryui.com/w/page/12138135/Widget-factory
http://ajpiano.com/widgetfactory/ (presentation)
If you want a more bare bone solution I'd go with either a regular Constructor + prototype setup to do things "properly" or use the Revealing Module Pattern to create a function that takes the element and any options as arguments and returns the public methods.
An example using the Revealing Module Pattern:
function myPlugin (element, options) {
var privateVar;
function privateFunc () {}
function publicMethod () {}
return {
publicMethodName: publicMethod
};
}
This pattern is a bit more tidy than a traditional prototypal set up, but does not take advantage of the prototype chain.
Edit: To clarify, when using any of these patterns you are supposed to create a new instance for each element/use.
It isn't necessarily a good idea to store any kind of stateful information in the plugin itself since it would be shared by all instances. One option is to store that data elsewhere, outside of the plugin.
The Plugins/Authoring page has a Data section which describes how to store information for use by your plugin on a per-element basis using the data() function.
Using data helps you keep track of variables and state across method
calls from your plugin. Namespacing your data into one object literal
makes it easy to access all of your plugin's properties from one
central location, as well as reducing the data namespace which allows
for easy removal if need be.
The example provided on the page uses the plugin pattern described in your post, but allows "instance" variables to be stored with the element they're associated with.
One key thing to remember when doing this is:
Always namespace your methods, events and data.
Edit:
It should be noted too, that in your example some of your functions expect $element as a parameter, but this isn't necessary since this will refer to the right thing when those functions are called through the plugin (because apply() is being called and setting the context to the correct this).