jQuery expose plugin functions - javascript

One of our old developers has built a jQuery plugin like so:
jQuery.fn.limelight = function(options) {
/*Skipped code here*/
jQuery(".spotlight-btn.back a").click( function (e) {
if(lastSelectedCastIndex - 1 >= 0) {
removeFromSpotlight();
lastSelectedCastIndex--;
e.preventDefault();
$.address.value(lastSelectedCastIndex);
ca$t.scroll(jQuery.jcarousel.intval(lastSelectedCastIndex), true);
switchTo(lastSelectedCastIndex);
}
return false;
});
function switchTo(i)
{
ca$t.scroll(jQuery.jcarousel.intval(i), true);
$.address.title($("#title_text").text());
putInSpotlight();
}
};
I've not done any jQuery plugin programming, but would like to expose the switchTo function so it can be called anywhere. How would I be able to do this?

This is probably overkill for your purposes, but it doesn't seem like your developer really understood or grasped the purpose of jQuery plugins.
You want a plugin to be somewhat generic where it can accept a selector and apply events, styles, dynamic html, whatever to the item(s) found in the selector. It looks like he wrote a "plugin" for a single purpose... maybe just to maintain some sort of organization.
Most plugins follow a form similar to this:
; (function ($) {
$.fn.limelight = function (method) {
var methods = {
//Initialize the plugin
init: function (options) {
return this.each(function () {
//Refactor to conform to plugin style
// $(this).click( function (e) {
// if(lastSelectedCastIndex - 1 >= 0) {
// removeFromSpotlight();
// lastSelectedCastIndex--;
// e.preventDefault();
// $.address.value(lastSelectedCastIndex);
// ca$t.scroll(jQuery.jcarousel.intval(lastSelectedCastIndex), true);
// switchTo(lastSelectedCastIndex);
// }
// return false;
// });
});
},
switchTo: function (i) {
//Refactor to conform to plugin style
// ca$t.scroll(jQuery.jcarousel.intval(i), true);
// $.address.title($("#title_text").text());
// putInSpotlight();
}
};
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.limelight');
}
};
})(jQuery);
//Following this pattern you'd be able to call your plugin like this.
$(".spotlight-btn.back a").limelight();
$(".spotlight-btn.back a").limelight("switchTo", 0);
Here's the official documentation on the subject: http://docs.jquery.com/Plugins/Authoring

Paste the switchTo() function within your <script></script> tags to make it a generally-available function.

You can use Jquery UI Widget Factory to create stateful plugins, which allows you to expose public methods (still avoiding global window scope) to be used even after the plugin has been instantiated
https://learn.jquery.com/plugins/stateful-plugins-with-widget-factory/

Related

Function/dom calls in javascript library

I'm trying to create a javascript library like jquery. I get how to create a normal library like so:
var lib=lib||(function () {
function privateFunction (alert ("hi");){};
return {
exampleAlert: function(input){
alert(input);
}
}
})();
Calling it like so:
lib.exampleAlert ("test");
This is like jquery
$.ajax(stuffhere);
My question revolves around jquery. It can call the dom like $('.class').hide() and have functions like $.ajax(stuffhere); in the same library. How can I do dom calls and a regular function call like the ajax one in the above example library?
Thanks in advance!! Have searched more days than I would like to admit.
DOM has nothing to do here, it is just up to jQuery implementation.
If you ask about having both lib() and lib.func() calls, then you can do the following to support both function types at the same time:
var lib = function(sel) {
return {
object: document.querySelector(sel),
text: function(val) {
if (val === undefined) {
return this.object.innerText;
} else {
this.object.innerText = val;
}
}
};
};
lib.ajax = function() {
console.log("AJAX imitation");
};
Now, you can do both:
lib("body").text("hi"); // jQuery-style setter
var text = lib("body").text(); // jQuery-style getter, returns "hi"
and
lib.ajax();
jQuery works exactly in the same way, but hundred times more complex.

Angular services with default values for non-existing attributes

Working on an Ionic application that performs both in Android and Windows.
There are services, such as Ionic's $ionicLoading, which we override functionality in order to work properly in windows:
angular.factory('$ionicLoading', function(){
return {
show: function (){...} // custom implementation
hide: function (){...} // custom implementation
}
});
But there are other services which we have to override only to not break the app.
In this cases it would be really useful to provide a service that won't do anything. For example:
angular.factory('$ionicExampleService', function(){
return {
*foo*: angular.noop // for operations
*bar*: promise // returns promise
}
});
Note: I know that a better way of doing this would be with a service that chooses between Ionic's implementation or a made one, but this is just for the sake of learning.
The ideal would be going even further, it would be magnificent to be able to return something even more bulletproof. Something like a generic flexible services:
angular.factory('$ionicPopup', function(){
return /*magic*/;
});
$ionicPopup.show({...}) // show was not defined
.then(foo); // won't break and will execute foo()
It is possible?
From what I understood you need to override implementation of existing services. You can do that with an angular service decorator.
A service decorator intercepts the creation of a service, allowing it to override or modify the behaviour of the service. The object returned by the decorator may be the original service, or a new service object which replaces or wraps and delegates to the original service.
For more information you can check angular documentation. One simple example would be:
app.factory('someService', function () {
return {
method1: function () { return '1'; }
method2: function () { return '2'; }
};
});
app.decorator('someService', function ($delegate) {
// NOTE: $delegate is the original service
// override method2
$delegate.method2 = function () { return '^2'; };
// add new method
$delegate.method3 = function () { return '3'; };
return $delegate;
});
// usage
app.controller('SomeController', function(someService) {
console.log(someService.method1());
console.log(someService.method2());
console.log(someService.method3());
});
EDIT: Question - How to override every method in the service?
var dummyMethod = angular.noop;
for(var prop in $delegate) {
if (angular.isFunction($delegate[prop])) {
$delegate[prop] = dummyMethod;
}
}
I hope that this helps you.
Using an evaluation for each assignment based on an object property, similar to this:
myVar = myObj.myPropVar === undefined ? "default replacement" : myObj.myPropVar;
Basically you're using a check for if the property has been defined, substituting a default value if it hasn't, and assigning it if it has.
Alternatively, you can use a modified version of the global function in Sunny's linkback to define defaults for all those properties you might assume to be undefined at specific points in your code.
function getProperty(o, prop) {
if (o[prop] !== undefined) return o[prop];
else if(prop == "foo") return "default value for foo";
else if(prop == "bar") return "default value for bar";
/* etc */
else return "default for missing prop";
}
Hope that helps,
C§
use var a = {}; to declare new variable.

How to get the name of a jQuery UI widget?

I need a way to read the name of a jQuery UI widget. I have subclassed the dialog widget into two subclasses, myDialog1 and myDialog2. I have created a destroyDialog function to destroy whichever dialog is active. There should be a way to determine the name of the widget instance.
What I want to do is something like this:
var destroyDialog = function() {
activeDialog[activeDialog.widgetName]("destroy");
}
But I don't see a way to get the widget name. For now I'm using ugly nested try-catch statements.
var destroyDialog = function() {
try {
activeDialog.dialog("destroy");
}
catch (e) {
try {
activeDialog.myDialog1("destroy");
}
catch (e) {
activeDialog.myDialog2("destroy");
}
}
}
You can get the widget name (and use it) by using
activeDialog.data("widgetName");
... as tdmartin refers to. So therefore:
activeDialog[activeDialog.data("widgetName")]("destroy");
But to get around this problem personally, I have written a plugin that will allow you to call a widget method without knowing what type the widget is. This will allow you to do:
activeDialog.callWidgetMethod('destroy');
It relies on you using jQuery UI 1.11+. If you are using <1.11, you can take out the "Skip this widget if it does not have the method" check, but the downside of that is that you will get an error if you attempt to call a method that a widget does not have.
Plugin code:
jQuery.fn.callWidgetMethod = function () {
var args = arguments;
var result = null;
if(!this || !this.length)
return this;
this.each(function (i,o) {
var compClass = $(this).data("widgetName");
var func = $(this)[compClass];
// Skip this element if it does not appear to be an initialised jQuery widget
if(!compClass || !func)
return true;
// Skip this widget if it does not have the method (the name of which will be in args[0])
// This relies on the 'instance' method provided in jQuery UI 1.11
if(args.length>1 && !$(this)[compClass]("instance")[args[0]])
return true;
result = func.apply($(this),args);
});
if(this.length>1)
return this;
else
return result;
};
If you standardize your namespace you could use a regex to match the name of the variable where your widget instance is stored (the name of the widget), returned by the $().data() method.
for (i in $(<your element>).data() ) {
if (i.match(/dialog/)) {
$(<your element>).data(i).destroy();
}
}

Calling a function in a jquery plugin

I am trying to get the object of a jquery plug in I'm making use of. I want to eventually be able to add features to the plugin to suit my needs. I am currently having a problem with calling one the functions I defined in the plug in. Here is the skeleton of the code I have:
;(function($) {
$.fn.myPlugin = function(o) {
.//Implementations here
.
.
var Ex = function(e){
//implementations here
};
};
})(jQuery);
The reason I want this function inside is because I want access to some of the variables defined. I would like be able to call function Ex from my html file but whatever I tried so far hasn't worked. An example of what I have tried is:
$.fn.myPlugin.Ex("x");
No return statement anywhere. I'm not great at javascript or jquery but I'm trying to learn. Any help in explaining what I'm doing wrong is appreciated.
Your plugin design pattern is wrong.
To achieve what you want, you can use this common one :
;(function($){
var methods = {
init: function() {
//stuff
},
function1: function() {
//stuff
},
function2: function(opt) {
//stuff
}
};
$.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);
}
};
})(jQuery);
With this structure :
$.fn.myPlugin(); will call init()
$.fn.myPlugin("function1"); will call function1()
$.fn.myPlugin("function2",option_exemple); will call function2(opt)
Disclaimer : I use this very often, but it's not mine. Can't remember where I found it*.
edit : http://docs.jquery.com/Plugins/Authoring , thanks to Beetroot-Beetroot for the reminder !

How can I emulate the Jquery UI API?

I've written basic jQuery plugins before, but I'm struggling to get my head around something more complex. I'm looking to emulate the API of jQuery UI, which works like this:
$('#mydiv').sortable({name: 'value'}); // constructor, options
$('#mydiv').sortable("serialize"); // call a method, with existing options
$('#mydiv').sortable('option', 'axis', 'x'); // get an existing option
I've tried the following:
(function($){
$.fn.myPlugin = function(cmd){
var config = {
default: 'defaultVal'
};
if(typeof cmd === 'object'){
$.extend(config, cmd);
}
function _foo(){
console.log(config.default);
}
if(cmd==='foo'){
return _foo();
}
this.each(function(){
// do default stuff
});
}
})(jQuery);
$('#myElement').myPlugin({default: 'newVal'});
$('#myElement').myPlugin('foo');
What I would like to see here is 'newval' being logged, but I'm seeing 'defaultVal' instead; the plugin is being called and started from scratch every time I call .myPlugin() on the element.
I've also tried using _foo.call(this) and some other variants. No joy.
In a way, I understand why this is happening, but I know that it must be possible to do it the same way as jQuery UI. I just can't see how!
(I appreciate that jQuery UI uses the widget factory to handle all of this, but I don't want to make that a requirement for the plugin.)
Perhaps what you want is this...
(function($){
var config = {
default: 'defaultVal'
};
$.fn.myPlugin = function(cmd){
if(typeof cmd === 'object'){
$.extend(config, cmd);
}
function _foo(){
console.log(config.default);
}
if(cmd==='foo'){
return _foo();
}
this.each(function(){
// do default stuff
});
}
})(jQuery);
$('#myElement').myPlugin({default: 'newVal'});
$('#myElement').myPlugin('foo');
Move the config variable outside the myPlugin function. This change will cause config to be initialized only once: when your plugin function is created.
You're declaring config during the function call rather than as a closure used by it. Try this:
(function($){
var config = {
default: 'defaultVal'
};
$.fn.myPlugin = function(cmd){
if(typeof cmd === 'object'){
$.extend(config, cmd);
}
function _foo(){
console.log(config.default);
}
if(cmd==='foo'){
return _foo();
}
this.each(function(){
// do default stuff
});
}
})(jQuery);
$('#myElement').myPlugin({default: 'newVal'});
$('#myElement').myPlugin('foo');
In addition, you could look into the jQuery data API for caching data, especially if you aren't going to have just one instance per page.

Categories

Resources