jquery making correct callback - javascript

how to correctly make callback in jquery plugin.
(function($) {
var parameter = {
first:'1',
second:'2',
call: $.noop
};
var something = 'yes';
var testf = function(){
// i neeed launch callback here;
var something_else = something + 'no';
alert(something_else)
}
$.fn.sadstory = function(options) {
if (options && typeof options === 'object')
{
$.extend(parameter, options);
}
testf();
return this;
}
})(jQuery);
and i need atccess var and owerwrite or making somthing else with him.
$('elm').sadstory({
call: function(){
this.something = 'no';
}
});
and result would by alert box with text nono instead of yesno, now to make this callback correctly.

i think you can do it like that:
$.fn.sadstory = function(options,callback) {
if (options && typeof options === 'object')
{
$.extend(parameter, options);
}
testf();
// example, var c is passed to callback function
var c= "abc";
callback(c);
return this;
}
you can call like
.sadstory({..},function(c) {
console.log(c) // logs "abc"
})
should also work as property of options

this.something doesn't exist. The only something is a variable with the scope of your testf method.
A solution is to pass an object as a parameter to the callback, and allow the callback to modify this object.
(function($) {
var parameter = {
first:'1',
second:'2',
call: $.noop
};
var something = 'yes';
var testf = function(){
// Initialize the string to a default value
var stringGenerationParams = { something: 'yes' };
// Allow the callback to modify the string generation params
parameter.call(stringGenerationParams);
// At this point, stringGenerationParams.something may have been
// modified by the callback function
var something_else = stringGenerationParams.something + 'no';
alert(something_else)
}
$.fn.sadstory = function(options) {
if (options && typeof options === 'object')
{
$.extend(parameter, options);
}
testf();
return this;
}
})(jQuery);
And now, this will work:
$('elm').sadstory({
call: function(e) {
e.something = 'no';
}
});

Related

Prototypes and callbacks

I'm now in the process of transforming several functions into prototypes and I'm stuck on callbacks.
Below is a minimal example of what I want to achieve:
WebSocketClient.prototype.send = function(t, data)
{
this.ws.send(data);
this.ws.onmessage = function(evt)
{
var msg = evt.data;
var jsonData = JSON.parse(msg);
if(jsonData["callback"] !== 'undefined' && jsonData["callback"] !== "") // jsonData = {callback:"on_test", data:[0,1,2]}
{
// How to transform callback into call ???
var fn = window[jsonData["callback"]]; // == undefined
if(typeof fn === 'function')
fn(jsonData["data"]);
}
};
};
function Test()
{
this.wc = new WebsocketClient();
// here ws.connect, etc.
}
Test.prototype.send = function()
{
this.wc.send(test, '{request:"get_data", callback:"on_test"')
}
Test.prototype.on_test = function(arr)
{
// ...
}
var test = new Test();
test.send();
I want to make a call to t.callback(data) but can't figure out how to do this?
I tried:
window[jsonData["callback"]]; // == undefined
window['Test.prototype.' + jsonData["callback"]]; // == undefined
window['Test.' + jsonData["callback"]]; // == undefined
There must be an error here:
Test.prototype.send = function()
{
// use 'this' instead of 'test'
// this.wc.send(test, '{request:"get_data", callback:"on_test"')
this.wc.send(this, '{request:"get_data", callback:"on_test"')
}
And since on_test() is defined on Test.prototype, call it this way:
WebSocketClient.prototype.send = function(t, data)
{
this.ws.send(data);
this.ws.onmessage = function(evt)
{
var msg = evt.data;
var jsonData = JSON.parse(msg);
if(jsonData["callback"] !== 'undefined' && jsonData["callback"] !== "") // jsonData = {callback:"on_test", data:[0,1,2]}
{
var fn = t[jsonData["callback"]]; // t will be available in this scope, because you've created a closure
if(typeof fn === 'function') {
fn(jsonData["data"]);
// OR, preserving scope of Test class instance t
fn.call(t, jsonData["data"]);
}
}
};
};
UPDATE: And be wary, that by calling fn(jsonData["data"]); you are loosing the original scope of the method. This way, this inside the on_test() method will point to global scope. If this is undesired, use call() (see corrected above).
If your function is in the global scope you could use
window.call(this, 'functionName', arguments)
In your case,
window.call(this, jsonData['callback'], jsonData['data'])
By doing that, the callback will be invoked with jsonData['data'] as a parameter.
If the function is within an object test, just use
test.call(this, 'on_test', jsonData['data'])

Geting undefined when passing in a parameter to a Module pattern function

$("#foo").on("click", function() {
amountItems.speek('heey')
})
var amountItems = (function(el) {
// var el = el;
return {
speek: function() {
alert(el)
}
}
}())
This is my first attempt to using a module pattern. basically when foo get's clicked i want the speek method inside the amountItems function to be called and I want to pass the string 'heey' to the method so it should alert 'heey' when foo is clicked. originally i wanted to pass something like $("#foo").text() but either way I get 'undefined'.
can you show me how to work with a jQuery object when it's passed into this type of function?
You just have the parameter for el in the wrong place. This works:
$("#foo").on("click", function() {
amountItems.speek('heey')
})
var amountItems = (function() {
return {
speek: function(el) {
alert(el);
}
}
}())
--edit--
Just in case you were wondering how the whole scope / private variables thing works:
$("#foo").on("click", function() {
amountItems.load('heey');
amountItems.speek();
})
var amountItems = (function() {
var el = ""
return {
load: function(str) {
el = str;
},
speek: function() {
alert(el);
}
}
}())
When you do this:
var amountItems = (function(el) {
// var el = el;
return {
speek: function() {
alert(el)
}
}
}())
You execute a wrapper function and assign amountItems with the inner object.
You don't pass a param(el) when you invoke this and therefore el is undefined.
amountItems is an object with a method called speek that doesn't except params.
The right way to do this is:
var amountItems = {
speek: function(txt) {
alert(txt);
}
};
$("#foo").on("click", function() {
amountItems.speek('heey')
})

Persisting values in the plugin object for a jQuery plugin

I'm trying to write a simple jQuery plugin that follows similar structure to the one below. The problem I'm having is that when I initialize the plugin the value plugin.myValue is set to 1 however when I try to access that value in the talk function it's undefined. Can anyone help me refine this plugin structure so when I set values on the plugin object they can be accessed in different methods.
Please keep in mind that the plugin below is not my actual use case it is just a simple example to illustrate the problem I'm having.
My actual use case would be very long because it is a jQuery carousel that I'm writing. If it will help I can provide that code however it's much longer and the below example follows the same flow. I would initialize this plugin with the following code:
$('#Div1').talk({ message: "test message" });
$('#Div1').talk('talk');
(function ($) {
$.fn.talk = function(method) {
var settings = {};
var $el = $(this);
var plugin = this;
var methods = {
init: function(options){
settings = $.extend(true, settings, this.talk.defaults, options);
return this.each(function() {
plugin.myValue = 1;
});
},
talk: function(){
helpers.talk(settings.message);
}
}
var helpers = {
talk: function(message){
if (plugin.myValue == 1)
{
alert(message);
}
}
}
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 in talk plugin!');
}
}
$.fn.talk.defaults = {
message: "default message"
}
})(jQuery);
When you define a plugin, this refers already to the jquery object (not the dom element), so I think your plugin var should go en each element, like this:
(function ($) {
$.fn.talk = function(method) {
var settings = {};
var $el = this; //Not need of $() here
var methods = {
init: function(options){
settings = $.extend(true, settings, this.talk.defaults, options);
return this.each(function(index, item) {
item.myValue = 1; //check this
});
},
talk: function(){
helpers.talk.call(this, settings.message); //talk needs scope
}
}
var helpers = {
talk: function(message){
var $elems = this;
if ($elems[0] && $elems[0].myValue == 1) //Something like this
{
alert(message);
}
}
}
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 in talk plugin!');
}
}
$.fn.talk.defaults = {
message: "default message"
}
})(jQuery);
I hope you get my idea. Don't forget that your selector might match to more than one element. You were stroring the data to the jquery object, and don't forget that it's a different one each time you call $("#yourDiv"), so your data was lost.
Note: It'd be cleaner to do $(item).data('myValue', 1); instead of item.myValue = 1; (and its proper retrieval), but it's a matter of choice
EDIT Option 2. This may look more similar to your code, but will work only when your selector only matched a single element
(function ($) {
$.fn.talk = function(method) {
var settings = {};
var $el = this; //Not need of $() here
var methods = {
init: function(options){
settings = $.extend(true, settings, this.talk.defaults, options);
$el.data("myValue", 1);
},
talk: function(){
helpers.talk(settings.message);
}
}
var helpers = {
talk: function(message){
if ($el.data("myValue") == 1)
{
alert(message);
}
}
}
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 in talk plugin!');
}
}
$.fn.talk.defaults = {
message: "default message"
}
})(jQuery);
Hope this helps. Cheers

How do I use a constructor in javascript

Here is my problem.
I am using Backbone js and every collection I have defined requires the same check on save or destroy. Except that the destroy success functions need to be passed an element to remove from the page when the destroy succeeds.
I didn't want to copy and paste the same code into every save or destroy method so I created this:
window.SAVE_TYPE_DESTROY = 'destroy';
window.SaveResponseHandler = function(el,type){
if (!type){
this.success = function() {
this._success();
};
}else if (type == window.SAVE_TYPE_DESTROY){
this.success = function() {
this._success();
$(el).remove();
};
}
};
SaveResponseHandler.prototype._success = function(model, response, options) {
if ((response.success * 1) === 0) {
persistError(model, {
responseText: response.message
}, {});
}
};
SaveResponseHandler.prototype.error = persistError;
var saveResponseHandler = new SaveResponseHandler();
And I use it like this:
destroy: function() {
var el = this.el;
var model = this.model;
this.model.destroy(new SaveResponseHandler(el,'destroy'));
},
change: function() {
this.model.set({
job_category_name: $($(this.el).find('input')[0]).val()
});
var viewView = this.viewView;
this.model.save(null, saveResponseHandler);
}
The problem is when success is called I get the following error:
Uncaught TypeError: Object [object Window] has no method '_success'
Any help will be much appreciated. I'm also open to any suggestions on better ways to handle this.
this inside of SaveResponseHandler.success isn't SaveResponseHandler, it's window.
window.SaveResponseHandler = function(el, type) {
var self = this;
if (!type) {
this.success = function() {
self._success();
};
} else if (type == window.SAVE_TYPE_DESTROY) {
this.success = function() {
self._success();
$(el).remove();
};
}
};
http://jsfiddle.net/ethagnawl/VmM5z/

javascript setting custom error handler to 3rd party plugins or modules

I am trying to set a custom error handler for 3rd party plugins/modules in my core library, but somehow, myHandler does not alert the e.message.
Can somebody help me please? thank you
Function.prototype.setErrorHandler = function(f) {
if (!f) {
throw new Error('No function provided.');
}
var that = this;
var g = function() {
try {
var a = [];
for(var i=0; i<arguments.length; i++) {
a.push(arguments[i]);
}
that.apply(null,a);
}
catch(e) {
return f(e);
}
};
g.old = this;
return g;
};
function myHandler(e) {
alert(e.message)
};
// my Core library object
(function(){
if (typeof window.Core === 'undefined') {
var Core = window.Core = function() {
this.addPlugin = function(namespace, obj){
if (typeof this[namespace] === 'undefined') {
if (typeof obj === 'function') {
obj.setErrorHandler(myHandler);
} else if (!!obj && typeof obj === 'object') {
for (var o in obj) {
if (obj.hasOwnProperty(o) && typeof obj[o] === 'function') {
obj[o].setErrorHandler(myHandler);
}
}
}
this[namespace] = obj;
return true;
} else {
alert("The namespace '" + namespace + "' is already taken...");
//return false;
}
};
};
window.Core = new Core();
}
})();
// test plugin
(function(){
var myPlugin = {
init: function() {},
conf: function() {
return this.foo.x; // error here
}
};
Core.addPlugin("myPlugin", myPlugin);
})();
// test
Core.myPlugin.conf(); // supposed to alert(e.message) from myHandler()
setErrorHandler in the above code doesn't set an error handler on a Function, as such. JavaScript does not give you the ability to change the called code inside a Function object.
Instead it makes a wrapped version of the function it's called on, and returns it.
obj.setErrorHandler(myHandler);
Can't work as the returned wrapper function is thrown away, not assigned to anything.
You could say:
obj[o]= obj[o].setErrorHandler(myHandler);
though I'm a bit worried about the consequences of swapping out functions with different, wrapped versions. That won't necessarily work for all cases and could certainly confuse third-party code. At the least, you'd want to ensure you don't wrap functions twice, and also retain the call-time this value in the wrapper:
that.apply(this, a);
(Note: you don't need the manual conversion of arguments to an Array. It's valid to pass the arguments object directly to apply.)

Categories