I have read & understood a question that describes how to add parameters to a function. I was wondering how, to make more modular code and plugins slightly more rigid. How would I go about creating default parameter values and user options within your plugin or your function?
$('.pluginAttachment').yourCoolPlugin({
parameter1: false, //User added
//the rest of the options
//Still adds the rest of the default options except above
});
I understand that these are variables but am not sure how to intertwine them into the overall function as a User parameter that can would take presidence over the default.
Here is an example of how I do it. I love doing this kind of thing. Makes the plugin easy for the user and easy to incrementally enhance.
(function ($) {
$.fn.yourCoolPlugin = function(options) {
// Extend our default options with those provided.
// Note that the first arg to extend is an empty object -
// this is to keep from updating our "defaults" object.
var opts = $.extend({}, $.yourCoolPlugin.defaults, options);
// Now your opts variable wil have either the defaults or values passed by the user.
DoSomething(opts.parameter1, opts.parameter2);
};
$.yourCoolPlugin.defaults = {
parameter1:false,
parameter2:"header"
};
})(jQuery);
Related
First of all I'm pretty new to javascript.
I have wrote a javascript plug-in that filters html records. Using Knockout.js, basically keyword/s are typed into a text box and the plug-in filters through the html records to reveal the records that contain the keyword/s.
I have wrote the javascript using the Module pattern, and would like the opportunity for the .js file to stand alone, whilst also giving the user the opportunity to change ways in which the filter works.
Here is a brief and simple overview of the structure of my .js file:
$(function(){
var s,
Filter = {
settings:{
//default settings
},
initialise: function(){
s = this.settings;
this.someFunction1;
this.someFunction2;
},
someFunction1: function(){
//
},
someFunction2: function(){
//
},
}
Filter.initialise();
});
As you can see, I have specified one function to be called - initialise. Within initialise I have set the variable s (declared at the same level as the module) to point to settings so that all sub functions of the module can access it.
What I want to know is, is it possible for the overriding settings to be passed via the html file that calls the .js file?
Ideally I would like there to be default settings for the plug-in, but give the user the option to specify/override certain settings unique to their need.
Any help would be appreciated. Thanks.
First up, there's a few peculiarities in your code.
One thing is that you have this.someFunction1; (and one for the second function) in your initialize function, but that doesn't quite do anything. It's just a statement.
Another thing is that you declare Filter as an object, but you're missing , (commas) between the various properties.
Finally, you declare someFunction2 twice (though I assume it's an copy/paste error?), and you're missing a semi-colon.
To find these problems you can use a JavaScript linting tool, for example JSHint. Try copy-pasting your question's code there and you'll see all those issues pop up.
If I fix those issues I get to this:
$(function(){
var s,
Filter = {
settings:{
//default settings
},
initialise: function(){
s = this.settings;
},
someFunction1: function(){
//
},
someFunction2: function(){
//
}
};
Filter.initialise();
});
Currently in this code, the Filter is only visible in the outer closure. To make it available to other bits of code you'll have to export it, either to your own namespace object or as a jQuery plugin.
The first option is possible, but will require a bit more work and assumptions about your setup. So let me stick to showing the second option. The jQuery's plugin tutorial is pretty good, but I've based the following code on this blogpost because it's a bit more complete IMO:
(function($) {
$.filter = function(element, options) {
var defaults = {
// default settings
}
var filter = this;
filter.settings = {};
var initialise = function() {
filter.settings = $.extend({}, defaults, options);
filter.element = element;
// code goes here
}
// This function is *public*, just save it in a 'var' instead of setting it
// as a property of 'plugin' to keep it private to your plugin.
filter.someFunction1 = function() {
//
}
filter.someFunction2 = function() {
//
}
initialise();
}
})(jQuery);
In this code the jQuery extend function is used above to extend default settings with user provided options.
You can call your plugin in its most basic form like this:
var options = { filterEmptyOptions: true, showTimeZone: false, /* etc */ };
$.filter($('#myDiv'), options);
Additionally you could save the result of last line and call the plugin's public methods later on.
I have special implementation for my grid.
For this I wrote some code in onSelectRow and loadComplete methods of jqGrid.
In onSelectRow I need to update a global array and in loadComplete method I have to access the global array and need to some manupulation in jqGrid.
Till then I am okay. I've already done this.
Now I want to extend these two method in such a way that is implementation will be generic (other grid can use this without writing any code).
For that I thought of below steps.
I want to add a new js (e.g: jquery.jqGrid.additional.js) in my html with jqGrid.js
I want to assign my global variable by the data array of jqGrid
In this js I want to check the value of multiselect of the grid
If the value is true, then I want to extend onSelectRow, loadComplete methods in such way that jqGrid execute both my methods and the code written in onSelectRow, loadComplete methods also.
For example I have preLoadComplete and postLoadComplete which need to be executed just before and after loadComplete method execution. Similarly this hold true for onSelectRow method also.
I wrote below code in jquery.jqGrid.additional.js and then didn't get the alert (1), alert (2) after jqGrid load.
It only execute the code written in loadComplete method of jqGrid.
var oldLoadComplete = $.fn.jqGrid.loadComplete;
$.jqGrid.extend({
loadComplete: function (){
var ret;
// do someting before
alert(1);
ret = oldLoadComplete.call (this);
// do something after
alert(3);
return ret; // return original or modified results
}
});
I tried a lot with this and spent many hours.
You are trying to extend $.jqGrid, not $.fn.jqGrid. In the first line that would mean that $.jqGrid doesn't even exist and that all elements would not have access to your modified .jqGrid plugin but still to the default.
Though I tried to extend $.fn with a "new" append method did not work. You might have to overwrite those methods explicitly. I could imagine that jQuery has some safety routines for itself and plugins built in in the $.extend method.
var oldLoadComplete = $.fn.jqGrid.loadComplete;
$.fn.jqGrid.loadComplete = function( ) {
alert('pre');
var ret = oldLoadComplete.apply(this, arguments); // In case any arguments will be provided in future.
alert('post');
return ret;
};
Edit:
Forget about all that. It is irrelevant. After looking into jqGrid's source code on GitHub a little, it is obvious that jqGrid does not even use something like $.fn.jqGrid.loadComplete. That is something you made up yourself. Instead, it creates another object which provides a list of possible properties and defaults which then is overwritten by custom defaults and finally by the function parameter you pass in with your call to jqGrid.
I found out that jqGrid triggers two events jqGridLoadComplete and jqGridAfterLoadComplete on your set of matched elements (i.e. this) before and after the call to the loadComplete callback respectively.
So basically, you cannot use your attempt. Instead, the only solution that comes to my mind is to overwrite the jqGrid "constructor" (i.e. $.fn.jqGrid) using the same method and apply listeners to this like so:
var oldJqGrid = $.fn.jqGrid;
$.fn.jqGrid = function( ) {
this.on('jqGridLoadComplete', function( jqEvt ) {
alert('Pre');
})
.on('jqGridAfterLoadComplete', function( jqEvt ) {
alert('Post');
});
return oldJqGrid.apply(this, arguments);
};
About your multiselect... I don't know what you're talking about. I have only very limited time myself and have to deny further assist for now.
Sincerely.
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!
I find that in my application I have the following pattern repeated a lot (see below code).
I have to call BindMyEvents(true) for the first load, and then BindMyEvents(false) for subsequent data retrieval.
It is lazily loaded in, so I don't want the data serialised into the HTML source. Is there a better way than having to pass in a boolean flag into my Bind() method? Is there a standard pattern to achieving this with knockout?
I was thinking should I just set viewAlertsModel.alerts = null inside the view model definition, then let the Bind function check this. If set to null then call the mapping method followed by the applyBindings()?
function BindMyEvents(initialMap) {
// get alerts, map them to UI, then run colorbox on each alert
$.getJSON("/Calendar/MyEvents/", {},
function (data) {
if ( initialMap ) {
// set-up mapping
viewAlertsModel.alerts = ko.mapping.fromJS(data);
ko.applyBindings(viewAlertsModel,$("#alertedEventsContainer")[0]);
} else {
// update
ko.mapping.fromJS(data, viewAlertsModel.alerts);
}
});
}
I would re-arrange your code for a different flow.
first - define you data once.
viewAlertsModel.alerts = ko.observable();
Second, bind your data
ko.applyBindings(viewAlertsModel,$("#alertedEventsContainer")[0]);
Third, now work with your data
$.getJSON("/Calendar/MyEvents/", {},
function (data) {
ko.mapping.fromJS(data, viewAlertsModel.alerts);
});
Steps one and two can be done during an initialization phase. The key here is first define viewAlertsModel.alerts as an observable.
Step three is your run-time code. Now, your initialization code is completely separate from your run-time code. This the more the normal knockout style.
edit
With regards to your comments about using ko-mapping, I use the following code
var tempVar = ko.mapping.fromJS(data);
viewAlertsModel.alerts(tempVar); // !important - do not use = operator.
This is the convention that most people use. Your use of ko-mapping is normally used for specialized situations.
I'm developing my first jQuery plugin that offers a number of optional callbacks. For example, there's an onChange callback that fires when the value of an input field changes.
For all of the settings, I have a defaults object that holds the default values, which is then merged with the instance settings: var settings = $.extend({}, defaults, options);
What is the best default value for these optional callbacks? Right now, I use null, but this requires that I check if the callback is a function or not all over the place:
if ($.isFunction(settings.onChange)) {
settings.onChange(...);
}
Is a better default an empty function? Or something else? What's the standard in jQuery plugin development?
Thanks!
You can use null as the default value and do:
var settings = $.extend({}, defaults, options);
settings.onChange && settings.onChange(/* arguments */);
I think that the best decision - use empty functions