0

I want to execute a callback function inside an object. I don't know if there is something wrong in the way I'm doing this.

I've googled for a solution, also searched on stackoverflow but couldn't find anything similar to the way I'm coding this.

PHPGateway.js

var PHPGateway = {

    opt_friendlyURL: true,
    opt_folder: 'ajax/',

    callback_function: null,

    useFriendlyURL: function (bool) {
        this.opt_friendlyURL = bool;
    },

    setFolder: function (folder) {
        this.opt_folder = folder;
    },

    send: function (service, method, data, callback) {
        var url,
            json_data = {};
        if (this.opt_friendlyURL) {
            url = this.opt_folder + service + '/' + method;
        } else {
            url = this.opt_folder + 'gateway.php?c=' + service + '&m=' + method;
        }
        if (data != undefined) {
            json_data = JSON.stringify(data);
        }
        this.callback_function = (callback == undefined) ? null : callback;
        $.ajax({
            method: 'POST',
            url: url,
            data: {data: json_data},
            success: this.ajax_success,
            error: this.ajax_error
        });
    },

    ajax_success: function (returned_object) {
        if (this.callback_function != null) {
            this.callback_function(returned_object.error, returned_object.data);
        }
    },

    ajax_error: function () {
        this.callback_function.call(false, {});
    }

};

Then inside the HTML file that loads PHPGateway.js, I've the following code:

<script>
    function submit_handler(event) {
        event.preventDefault();
        form_submit();
    }
    function form_callback(error, data) {
        if(error == null) {
            alert(data.text);
        }
    }
    function form_submit() {
        var data = {
            status: $('#inStatus').val(),
            amount: $('#inAmount').val(),
            id: $('#inBudgetID'). val()
        }
        PHPGateway.send('budget', 'status', data, form_callback);
    }
    $('form').one('submit', submit_handler);
</script>

I get an error on this.callback_function(returned_object.error, returned_object.data);, the error is Uncaught TypeError: Object # has no method 'callback_function'.

  1. What am I doing wrong?
  2. Is this the best way to do it?

Thank You!

Based on minitech answer, I've updated PHPGateway.js like this. I've omitted the parts that weren't updated.

var PHPGateway = {

    // Omitted code

    send: function (service, method, data, callback) {
        var url,
            json_data = {},
            that = this;
        if (this.opt_friendlyURL) {
            url = this.opt_folder + service + '/' + method;
        } else {
            url = this.opt_folder + 'gateway.php?c=' + service + '&m=' + method;
        }
        if (data != undefined) {
            json_data = JSON.stringify(data);
        }
        this.callback_function = (callback == undefined) ? null : callback;
        $.ajax({
            method: 'POST',
            url: url,
            data: {data: json_data},
            success: function(data, textStatus, jqXHR) {
                that.ajax_success(data, textStatus, jqXHR);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                that.ajax_error(jqXHR, textStatus, errorThrown);
            }
        });
    },

    ajax_success: function (data, textStatus, jqXHR) {
        if (this.callback_function != null) {
            this.callback_function(true, data.data);
        }
    },

    ajax_error: function (jqXHR, textStatus, errorThrown) {
        this.callback_function.call(false, {});
    }

};

Now it works!!!

6
  • 4
    This seems to work fine for me. Is this code in the middle of some other code? Commented Dec 19, 2013 at 16:47
  • Yeah, this works fine. Your problem is somewhere else. As for "the best way" that's opinion based. Commented Dec 19, 2013 at 16:48
  • This should work fine. Are you maybe using a method of some other object as myCallbackFunction, and it throws a similar error when called, but not the same one? (Or maybe it is the same one and you’re actually doing some kind of recursive thing?) Commented Dec 19, 2013 at 16:49
  • This works, but is rather strange? You have a property that is null, and inside the function you overwrite that property with an external function that is not even called from within the object, you could just call the callback, callback('Hello World!') and it would be exactly the same ? Commented Dec 19, 2013 at 16:50
  • Wait, yeah, that’s actually pretty impossible (unless you have an unwritable property/sealed object/frozen object outside of strict mode, or a proxy). Please try to reduce it to a case that actually displays the problem. Commented Dec 19, 2013 at 16:55

2 Answers 2

2

In your call to $.ajax, you need to add a context option:

$.ajax({
    method: 'POST',
    url: url,
    data: {data: json_data},
    context: this,
    success: this.ajax_success,
    error: this.ajax_error
});

Your this variable in your Ajax success and error handlers are not pointing to the object you think they are. The context option to $.ajax() sets which object this points to in the Ajax callbacks.

Sign up to request clarification or add additional context in comments.

3 Comments

Greg, thank you for your response. Your method is simpler, but minitech's answer helped me also to understand the scope issue, even if I don't use jQuery. I wish I could mark both questions as accepted.
Heh, oops! Good answer.
@Gonzalo Massa: No problem. :) Glad I could help.
1

Here’s your problem:

$.ajax({
    method: 'POST',
    url: url,
    data: {data: json_data},
    success: this.ajax_success,
    error: this.ajax_error
});

When you set success and error to methods on this, they don’t keep their this. When a JavaScript function is called, it gets bound a this:

someFunction(); // this is undefined or the global object, depending on strict
someObject.someFunction(); // this is someObject

The built-in .call, .apply, and .bind of Function objects help you override this.

In your case, I think jQuery binds this to the Ajax object – a good reason to both not use jQuery and always use strict mode.

If you can guarantee or shim ES5 support, bind is an easy fix:

$.ajax({
    method: 'POST',
    url: url,
    data: {data: json_data},
    success: this.ajax_success.bind(this),
    error: this.ajax_error.bind(this)
});

Which is equivalent to this if you can’t:

var that = this;

$.ajax({
    method: 'POST',
    url: url,
    data: {data: json_data},
    success: function() {
        that.ajax_success.apply(that, arguments);
    },
    error: function() {
        that.ajax_error.apply(that, arguments);
    }
});

And now, a tip for you: don’t namespace, and if you do, don’t use this. this is great for objects that are meant to be constructed. What would seem more appropriate is something like this, if you really have to:

var PHPGateway = (function() {
    var callbackFunction;
    var options = {
        friendlyURL: true,
        …
    };
    …

    function send(service, method, data, callback) {
        …
    }
    …

    return { send: send };
})();

3 Comments

Thank you for taking your time to answer this, you were very helpful. Based on your response, I've updated the code and now works. The full code doesn't fit on this comment, I've updated my question to show the final code. And thank you for the tip, I don't understand the line return {send:send}, what is that for?
@GonzaloMassa: The idea is that you don’t create the object that holds the things in your namespace until the very end, so all the functions are part of the scope for easy access instead of inside another object. But you still need to expose it, and that part exposes it in the same way you originally had everything.
So I should also add useFriendlyURL and setFolder on the return object? Both functions should be able to be called from outside.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.