Search code examples
javascriptscopexmlhttprequesturl-routing

How to keep scope within an XHR request?


So I am building a Javascript router and building a route like this:

route('/user/{name}', 'page', function() {
    this.text = 'User: ' + this.name;
});

The scope of the function is the current route, so I can manipulate the current route here (this.text is what the view is looking for).

Now my next step is to include an XHR request within the route, which would look something like:

route('/user/{name}', 'page', function() {
    this.text = 'Loading';

    var request = new XMLHttpRequest();
    request.open('GET', '/api/user', true);

    request.onreadystatechange = (function() {
        if (this.readyState === 4) {
            if (this.status >= 200 && this.status < 400) {
                var data = JSON.parse(this.responseText);
                // here is the issue: 'this' now refers to
                // the XHR request and not the scope of the router
                this.age = data.age;
                this.gender = data.gender;
            } else {
                this.text = "Error";
            }
        }
    })/* .bind(this); */ // keeping scope like this overwrites the XHR

    request.send();
    request = null;
});

The issue here is that I need to access the XHR scope and my router's scope. Using .bind at the end of the onreadystatechange would over write the XHR scope, and not setting it overwrites the router's scope.

so wat do? Is there anything cleaner than var that = this; -- surely there must be a way?


Solution

  • The simplest (and very clear way) is to keep the reference to route's scope like this:

    var that = this;
    

    Also you can set the scope using .bind() and access request properties directly from reqest variable.

    And for your example (with bind helper function, to support old browsers):

    var bind = function(fn, context) {
        return function() {
            fn.apply(context, arguments);
        };
    };
    
    request.onreadystatechange = bind(function() {
      if (request.readyState === 4) {
        if (request.status >= 200 && request.status < 400) {
          var data = JSON.parse(request.responseText);
          this.age = data.age;
          this.gender = data.gender;
        } else {
          this.text = "Error";
        }
      }
    }, this);