0

I am writing a Javascript Library which has the following code:

Constructor (Creating intial keys and creating a XMLHTTP Request object):

function hrce(key) {    
    var about = {
        Version: 0.1,
        Author: "AAA",
        Created: "Spring 2014",
        Updated: "March 2014"
    };

    if (key) {

        this.xhr = "";
        this.xhrdata = "";
        this.xhrmethod = "";
        this.xhrurl = "";
        this.xhrquery = "";

        //init with the current avaliable information
        this.key = key;
        this.protocol = "http:" === document.location.protocol ? "http://" : "https://";
        if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
            this.xhr = new XMLHttpRequest();
        }
        else {// code for IE6, IE5
            this.xhr = new ActiveXObject("Microsoft.XMLHTTP");
        }

        return this;
    } else {
        // No 'id' parameter was given, return the 'about' object
        return about;
    }
};

Here are my Library functions:

hrce.prototype = {
    load: function() {
        if (this.xhr && this.xhr != "" && this.key && this.key != "") {
            this.xhrdata = [{"access_key": this.key}];
            this.xhrurl = this.protocol + "localhost/hrce/v1/action/hsio/";
            this.xhr.onreadystatechange = this.initilizer();
            this.xhr.open("POST", this.xhrurl, true);
            this.xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            this.xhrquery = "access_key=" + this.key;
            this.xhr.send(this.xhrquery);
        }
        return this;
    },
    initilizer: function() {
        if (this.xhr.readyState == 4 && this.xhr.status == 200)
        {
            console.log(this.xhr.responseText);
        }
    }
};

now if i call for example: hrce("f07c7156").load(); Ajax call goes successfully but its not calling my this.xhr.onreadystatechange = this.initilizer(); call in load prototype function. Whats wrong in it?

Seeker
  • 1,877
  • 4
  • 32
  • 56
  • When every single line starts with `this.xhr`, it would be great if you could be a little more specific as to what line the error is on ? – adeneo Mar 13 '14 at 20:49
  • @adeneo I just updated it its not calling my `this.xhr.onreadystatechange = this.initilizer();` binding. – Seeker Mar 13 '14 at 20:51
  • @adeneo Update my question again now error is gone but its not calling my `initilizer` call on `onreadystatechange` – Seeker Mar 13 '14 at 20:53
  • Would `this.xhr.onreadystatechange = this.initilizer();` resolve to basically `XMLHttpRequest.initializer()`? Since the `this` would be in the scope of the ajax request? – zero298 Mar 13 '14 at 20:56
  • Well, use another [library](http://jquery.com/). :x – loveNoHate Mar 13 '14 at 20:56
  • @dollarVar client don't want to make his library depend upon `jQuery`. Otherwise that would be a great option just to use `$.ajax` – Seeker Mar 13 '14 at 20:58
  • @zero298 Yes it does you can see it in the constructor of library – Seeker Mar 13 '14 at 20:58
  • 2
    It should be `this.xhr.onreadystatechange = this.initilizer;`, reference the function, not call it! – adeneo Mar 13 '14 at 21:00
  • @adeneo if i use that as reference it gives me `TypeError: this.xhr is undefined` error – Seeker Mar 13 '14 at 21:01
  • That's because inside the callback function for an ajax request `this` is not your object. – adeneo Mar 13 '14 at 21:05
  • @adeneo what could be the alternate then to resolve this? – Seeker Mar 13 '14 at 21:09
  • 1
    You're using a pattern that doesn't really work well with this kind of functionality, where you need access to the XHR object all along the way, and you're splitting it up in prototypes and what not. The solution to the current problem would be to either change the value of `this` with `bind`, `call` or `apply`, or to reference the object inside the callback, and not `this`. – adeneo Mar 13 '14 at 21:11
  • @adeneo can you give me a fiddle actually its late night here and my mind is really stuck at this point. – Seeker Mar 13 '14 at 21:29
  • 1
    Something like this...http://jsfiddle.net/TRNCFRMCN/Kk2E3/ – loveNoHate Mar 13 '14 at 21:34
  • 2
    @dollarVar - exactly, but I think call() would also call the function right away, and not when the event happens, so an anonymous function would probably be neccessary, something like this -> http://jsfiddle.net/Kk2E3/1/ – adeneo Mar 13 '14 at 21:37
  • @adeneo Yupp, that did it. I`m confused about that *vanilla Javascript* Ajax, does the `onreadystatechange` get called three times? :o – loveNoHate Mar 13 '14 at 21:44
  • @dollarVar - it can be, the readyState event fires whenever there's a change, and there are multiple scenarios where it would fire, but with different statusCodes, 0: request not initialized, 1: server connection established, 2: request received, 3: processing request, 4: request finished and response is ready – adeneo Mar 13 '14 at 21:51
  • @adeneo Yep, 4, that`s what I counted now. Thanks! – loveNoHate Mar 13 '14 at 21:54
  • @dollarVar i updated my code according to your implementation it still doesn't work. It works on jsfiddle but not on my pc. Same is the case with adeneo code. I changed the `this.xhrurl`. – Seeker Mar 13 '14 at 23:22
  • Hmm, you have the same `xhrurl` as before now? What does the `console` say? Go with @adeneo's code! – loveNoHate Mar 13 '14 at 23:31
  • @dollarVar Yes here is the fiddle of mycode: http://jsfiddle.net/KCPYy/1/ . console doesn't output anything – Seeker Mar 13 '14 at 23:43
  • Yeah, well, the code does not work like this with `localhost` on JSFIDDLE. You gotta be strong now and do it on your machine ;) (BTW, it's late, you wanted to go to sleep;) – loveNoHate Mar 13 '14 at 23:55
  • @dollarVar True obviously its not gonna work as localhost is my machine :D. But problem is its not working on my machine. Request is gone successfully now called through self but still its not working. Yep its late and i still am stuck on this issue lol – Seeker Mar 14 '14 at 00:04
  • 1
    What do you mean with "Request is gone successfully" but "it's not working"? Does @HMR's solid solution solve it? – loveNoHate Mar 14 '14 at 00:20
  • @dollarVar Yep HMR's solution worked perfectly. Actually i was not able to understand the closure concept. BTW above meaning was that if i see in console ajax Request successfully completed. But it wasn't showing me the responseText. Once again thank you both as well because you both made me think about that Inheritance concept in the JS. – Seeker Mar 14 '14 at 00:27

1 Answers1

2
  1. You're not using the function as a constructor.
  2. You're not assigning a bound function or passing a closure to onreadystatechange.

For the first one you have to decide, do you want the function to return an object or do you want the function to work as a constructor function. If you want a constructor then see the sample code below (I capitalized the function name as constructor functions should start with a capital).

For the second one you have to pass a closure or use bind, I use passing a closure in sample below.

function Hrce(key) {
  var about = {
    Version: 0.1,
    Author: "AAA",
    Created: "Spring 2014",
    Updated: "March 2014"
  };

  if (key) {
    this.xhr = "";
    this.xhrdata = "";
    this.xhrmethod = "";
    this.xhrurl = "";
    this.xhrquery = "";

    //init with the current avaliable information
    this.key = key;
    this.protocol = "http:" ===
            document.location.protocol ? "http://" : "https://";
    if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
      this.xhr = new XMLHttpRequest();
    }
    else {// code for IE6, IE5
      this.xhr = new ActiveXObject("Microsoft.XMLHTTP");
    }
    //constructor funcitons do not need to return this
    return this;
  } else {
    // No 'id' parameter was given, return the 'about' object
    return about;
  }
}
;

Hrce.prototype = {
  load: function() {
    if (this.xhr && this.xhr != "" && this.key && this.key != "") {
      this.xhrdata = [{"access_key": this.key}];
      this.xhrurl = this.protocol + "localhost/hrce/v1/action/hsio/";
      //note that initilizer returns a function that has a closure
      //  scope with the current instance
      this.xhr.onreadystatechange = this.initilizer(this);
      this.xhr.open("POST", this.xhrurl, true);
      this.xhr
        .setRequestHeader("Content-type"
        , "application/x-www-form-urlencoded");
      this.xhrquery = "access_key=" + this.key;
      this.xhr.send(this.xhrquery);
    }
    return this;
  },
  initilizer: function(me) {
    //returning a function used as closure
    // the variable me is the current instance of Hrce
    return function(){
      if (me.xhr.readyState == 4 && me.xhr.status == 200)
      {
        console.log(me.xhr.responseText);
      }
    }
  }
};

var connector = new Hrce("f07c7156");
connector.load();

More info about constructor functions, prototype and what the this variable represent can be found in this answer.

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
  • Perfect! Thats what i was looking for thanks mate for the answer and specially the link of your answer in which you gave the introduction of the Prototyping. – Seeker Mar 14 '14 at 00:22
  • Hey, nicely done (plus one;). He was calling it with `new` at first, because the *whole* thing would not have worked like this (wanted to comment on that first). However, do you know why **[@adeneo's solution](http://jsfiddle.net/Kk2E3/1/)** didn't "work" in the end? – loveNoHate Mar 14 '14 at 00:47
  • @dollarVar The code works but the site doesn't allow POST request or the header. You can see the request fail in firebug or Chrome (press F12). Changed the POST to `this.xhr.open("GET"` and commented out `this.xhr.setRequestHeader("Content-t...` Then it works. His code is basically the same as mine but he creates a closure with an anonymous function and that can trap large variables you never use. – HMR Mar 14 '14 at 00:56
  • @HMR One more question i have. If i use your implementation. Here is the fiddle: http://jsfiddle.net/g6Qeb/1/ . Now my Question is that i am calling the function of track of Hrce. In your answer i set the cookie in the intilizer function. i call track right after that load but it doesn't get the cookie. But if i call track on console separately it works. What is the missing point ? am i not using the inheritance correctly? – Seeker Mar 14 '14 at 01:28
  • @PHPSeeker A quick look at the code let me think you assume the xhr request is synchronous. When you call load the xhr request is made but when you call track the load request is almost certainly not finished yet so the cookie hasn't been set yet. You can use callbacks, promises or use a mediator to solve this. The easiest way would be callback, the most maintainable would be promise or mediator but that's kind of complicated. – HMR Mar 14 '14 at 01:47
  • @HMR can you give me a example of calling the callback with my implementation ? just update the fiddle i used so that i can use it as a reference for other functions? It will be really helpful. Sorry i am actually new to JS library writing so thats why i am kind of newbie. – Seeker Mar 14 '14 at 02:53