0

I am unable to call this.RenderDeals() from inside my Post.success function. I assume this has something to do with scope? Could someone shed some light on the issue, and possibly suggest a workaround. I have tried using prototype and module pattern, both without any luck.

FYI Post is an $.ajax wrapper that returns a jQuery Deferred object.

    function Deals() {
        this.template = '#trTemplate';
        this.container = '#containerTable';
        this.GetDeals = function () {
            Post('Deal.svc/GetDeals')
            .success(function (result) {
                this.RenderDeals(result);
            });
        };
        this.RenderDeals = function (deals) {
            $(this.template).tmpl(deals).appendTo(this.container);
        }
    }


    var deal = new Deals();
    deal.GetDeals();

UPDATE:

Ok, so I added the following line just above the GetDeals function:

var me = this;

and instead call

me.RenderDeals(result);

Appears to work correctly, but I'm not sure why.

3
  • That works because you've defined 'me' and then used it. But using 'this' directly is going to give you an instance of something else, because you're in a different context/scope. Commented Mar 18, 2011 at 23:56
  • just an FYI thing, in javascript idioms, a function starting with a capital means it is a constructor function. Not a problem or anything, but it just looks odd to someone who is a javascript guy but not a .net guy Commented Mar 18, 2011 at 23:56
  • @Matt maybe I'll start a revolution... Commented Mar 19, 2011 at 0:34

5 Answers 5

5

The this reference in your "success" function almost certainly isn't what you think it is. Try this change:

function Deals() {
    var instance = this;

    this.template = '#trTemplate';
    this.container = '#containerTable';
    this.GetDeals = function () {
        Post('Deal.svc/GetDeals')
        .success(function (result) {
            instance.RenderDeals(result);
        });
    };
    this.RenderDeals = function (deals) {
        $(this.template).tmpl(deals).appendTo(this.container);
    }
}

By stashing a reference to this in the "instance" variable, your handler will have a guaranteed way of getting back to the relevant instance when calling the other function ("RenderDeals").

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

Comments

2

Firstly, there are no classes in javascript, only objects. This may sound strange, but you can still do OOP without them in javascript :) I suggest you read up on this.

You are correct about the scope issue, the this keyword has some strange behavior. Read this for some more info.

Look into using the prototype attribute of any function object. I prefer to do my javascript OOP in the following fashion:

function Deals(){
    //this is your constructor
    var me = this;
    //this way you can avoid having to search the DOM every time you want to reference
    //your jquery objects.
    me.template = $('#trTemplate');
    me.container = $('#containerTable');
}
Deals.prototype.getDeals = function(){
    var me = this;
    Post('Deal.svc/GetDeals')
    .success(function (result) {
        me.RenderDeals(result);
    })
}
Deals.prototype.renderDeals = function(){
    var me = this;
     me.template.tmpl(deals).appendTo(me.container);
}

By declaring var me = this; at the top of each prototype function, you create a closure that refers to the object of interest, even inside of a callback :)

2 Comments

Oh yes, forgot to mention that you would use the Deals object like this: var deals = new Deals(); deals.renderDeals();
Thanks for the alternative pattern. To be honest, I don't know if I really need to be doing any of this. I'm never going need many instances of the object, I was just trying to encapsulate a particular piece of my pages functionality to make it cleaner and more manageable.
0

Why not define RenderDeals() outside of Deals()?

    function RenderDeals(dealsObj, dealsData) {
        $(dealsObj.template).tmpl(dealsData).appendTo(dealsObj.container);
    }

Then instead of this:

this.RenderDeals(result);

Do this:

RenderDeals(this, result);

3 Comments

Because I am trying to encapsulate the functionality in a single class.
Maybe he doesn't want it to be globally visible?
Sorry, that wasn't obvious because it wasn't specified in the question.
0

Try this:

var Deals = {
    template: $('#trTemplate'),
    container: $('#containerTable'),
    init: function (){
        return Deals;
    }
    GetDeals: function () {
        Post('Deal.svc/GetDeals')
        .success(function (result) {
            Deals.RenderDeals(result);
        })
    },
    RenderDeals = function (deals) {
        Deals.template.tmpl(deals).appendTo(Deals.container);
    }
}

var deal = Deals.init();
deal.GetDeals();

2 Comments

Maybe he really wants to be able to construct many of these instances.
this will work, but imo pointy has a better answer by using a closure to capture the value of this.
0

You are right, it is a scoping issue. Pointy has the right answer, but I thought I could fill in a bit of what is going on.

this in javascript is an implicit variable that is set to whatever the function is attached to by default (or the global object if it isn't attached to anything). When you pass your function as a param into Post#success, it is not attached to any instance, let alone the current instance of Deals.

Pointy has the best answer here, what he is doing is creating an explicit variable that captures the implicit variable of this of deals. One of the coolest features of javascript is that everything that is in scope of a function definition will also be in scope for a function invocation, so by making that variable, you are able to access it again later, whenever and however that anonymous function is called.

Comments

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.