0

I'm pretty sure this is a scoping issue but I have a backbone view that is using Bootbox (a small library that extends twitter's Bootstrap) to prompt for confirmation before saving a model to a database. The view is responsible for catching the SAVE button click at which point the Bootbox dialogue is supposed to pop up and prompt for confirmation. See below:

    window.StartView = Backbone.View.extend({

    initialize: function () {
        this.render();
    },  

    events: {
        "click .nextstage"   : "nextstage",
        "click .reshuffle"   : "render",
        "click .drilldone"   : "drilldone"
    },  

    drilldone: function() {

      bootbox.confirm("Record Results?", function(result) {

      result ? this.saveResults : alert("canceled") ;

    }); 

    },

The problem is this.saveResults never runs. I tried doing an "alert(this)" in place of it and I got back "Window" which makes sense because the Bootbox library was defined in the main HTML document. So how do I get the Bootbox call to then make a callback to my View's saveResults method? I have to pass a reference to the method into the Bootbox confirm method somehow right?

user1813867
  • 1,045
  • 1
  • 9
  • 17

2 Answers2

1

Just scope the context in a variable, and call the function. (without parentheses () it won't run)

drilldone: function() {
    var self = this;

    bootbox.confirm("Record Results?", function(result) {

        result ? self.saveResults() : alert("canceled") ;

    }); 

},

Javascript uses scope chains to establish the scope for a given function. When you create a function, you create a new scope, and this scope has access to every variable declared within and every variable declared on their parents (think of it as a scope chain going all the way up until the global scope).

When there's no setted this, this become the global object (window in the browser). You can change dynamically the value of this whitin a function by using call, apply or bind. This is what jQuery is doing in most of its callback (this may be why you tought window was this - but that's unrelated).

By assigning var self = this, you kept a reference to the this object available in the bootbox callback scope as a variable.

This is very light overview. You may be interested in reading this other stackoverflow answer: What is the scope of variables in JavaScript?

There's also a free book called JavaScript Enlightment who's touching the subject.

Community
  • 1
  • 1
Simon Boudrias
  • 42,953
  • 16
  • 99
  • 134
  • OK, that works. But maybe you can clarify why? I thought in this case the Bootbox object was in the "Windows" scope because that's where it was defined. So when you make a local variable in the StartView.drilldone scope called "self", how is it that the Bootbox object can access that? – user1813867 Sep 09 '13 at 21:47
  • added a section to my answer about the behavior of the scopes in javascript – Simon Boudrias Sep 09 '13 at 23:10
  • I read the book you linked all the way to Chapter 8. It was a good read, thank you. And I plan to keep reading tomorrow. BUT, it did not answer my question. Here is a quote directly from the book: "Scope is determined during function definition, not invocation." So I am wondering if the bootbox.confirm method is defined in the HEAD object, how does it have access to the "self" variable I created in my View object? – user1813867 Sep 10 '13 at 06:07
  • I just figured it out. It's not the bootbox.confirm method that's accessing the "self" variable, it's the function(result) callback. And that scope was defined within the same scope that contains the "self" variable so it checks out. – user1813867 Sep 16 '13 at 21:03
0

I've never used Backbone but this does not look Backbone-specific but a scoping issue as you're suggesting. I'd try:

window.StartView = Backbone.View.extend({

  initialize: function () {
      this.render();
  },  

  events: {
      "click .nextstage"   : "nextstage",
      "click .reshuffle"   : "render",
      "click .drilldone"   : "drilldone"
  },  

  drilldone: function() {
    var ctx = this;
    bootbox.confirm("Record Results?", function(result) {
    result ? ctx.saveResults : alert("canceled") ;
  }); 
},

This is mildly suboptimal since reassigning this is a bit of memory leak but the impact will most likely be negligible.

Marcin Wyszynski
  • 2,188
  • 1
  • 17
  • 16