0

I have some Ajax that gets info from a database and stores it in an array called targetVocab. This info is used to set the value of vocab when setVocab() is called. This works fine the first time, but when the play again button is hit and setVocab is called a second time, it doesn't work. The second time it appears that targetVocab is empty. This is my simplified script...

var targetVocab;
var vocab;
var request = new goog.net.XhrIo();

goog.events.listen(request, "complete", function(){

    if (request.isSuccess()) {
        response = request.getResponseJson();
        targetVocab = response['targetVocab'];
        setVocab(targetVocab);
    }

});

request.send("load_vocab.php");

var setVocab = function(targetVocab) {
    vocab = targetVocab;
}

var getVocab = function() {
    return vocab;
}


goog.events.listen(playAgainButton, ['mousedown', 'touchstart'], function(e) {
    getVocab();
});

UPDATE: I've come to realize that whatever I do to vocab is similarly applied to targetVocab. My original idea was that I could splice elements from vocab throughout the app and then reset vocab to it's original state by setting it again to targetVocab. Is there some way to set vocab equal to targetVocab without the two remaining connected in this way?

niftygrifty
  • 3,152
  • 2
  • 24
  • 45
  • What is `response['targetVocab'];`? I suspect it's an array of strings so you can copy it with: `vocab = targetVocab.concat([]);` If it's an array of object then you need to deep copy it, not sure if the closure library has a method for this. – HMR Jul 10 '13 at 08:42
  • 1
    In case you're trying to copy an array of objects or object you can try `goog.object.unsafeClone` http://docs.closure-library.googlecode.com/git/closure_goog_object_object.js.html but it could end you in a never ending loop, closure library doesn't seem to have anything like jQuery extend. – HMR Jul 10 '13 at 08:51
  • It's an array of objects which are, in turn, associative arrays. Deep copying you say... – niftygrifty Jul 10 '13 at 08:53
  • That did it! `goog.object.unsafeClone` was the answer! If you can post it in an answer I'll go ahead and accept it. – niftygrifty Jul 10 '13 at 09:01
  • I just finished formulating my anser :) – HMR Jul 10 '13 at 09:07
  • 1
    I've updated my answer with some lighter solutions. You can copy the array to a new array if you don't change the item values or you can store the json response text in targetVocab and set the value of vocab with the parsed result of targetVocab when you want to re create it. – HMR Jul 10 '13 at 09:31

2 Answers2

2

You need to define the function setVocab differently.

A second thing I just realized: you should write 2 functions one set function and one GET function this waay the will not splice anymore.

e.g.

 function setVocab(targetVocab){
    vocab = targetVocab;       
}

This way you ensure that you really pass it the targetVocab as a variable. And now the GET function accordingly:

 function getVocab(){
    return vocab;
}

OR put it in one (not recommended)

function setVocab(targetVocab){
    if(targetVocab!=""){
     vocab = targetVocab;
    }else{
     return vocab;
    }
}

Now depending on what you have implemented call

goog.events.listen(playAgainButton, ['mousedown', 'touchstart'], function(e) {
getVocab();

});

or if you implemented the "not recommended" code then use this

goog.events.listen(playAgainButton, ['mousedown', 'touchstart'], function(e) {
setVocab("");

});

and

goog.events.listen(request, "complete", function(){

if (request.isSuccess()) {
    response = request.getResponseJson();
    targetVocab = response['targetVocab'];
    setVocab(targetVocab); //chaged here
}

});

Setup
  • 331
  • 2
  • 20
  • That doesn't seem to be working for me. Even if I pass function an argument like you suggest, the variable `targetVocab` is spliced whenever `vocab` is spliced. – niftygrifty Jul 10 '13 at 07:34
  • can you show me the new code? I have edited my post, use 2 functions to set and get the variable so they will not splice... good habbit is not to mix this up. – Setup Jul 10 '13 at 07:42
  • Thanks very much for your help. I've edited to incorporate your suggestions, but my problem persists. – niftygrifty Jul 10 '13 at 07:51
  • you still need to use goog.events.listen(playAgainButton, ['mousedown', 'touchstart'], function(e) { getVocab(); //here!! }); the GET is important :) – Setup Jul 10 '13 at 07:53
  • Oops! I think I've edited the way your suggest now, but still having the same problem. – niftygrifty Jul 10 '13 at 07:59
  • I am not sure wether it will make a difference, but it is worth a try: [this](http://stackoverflow.com/questions/336859/javascript-var-functionname-function-vs-function-functionname) What I am trying to say: Try implementing the functions as I did, so they are defined at parse-time. – Setup Jul 10 '13 at 08:05
1

I think your problem is caused by setting vocab to targetVocab. In JavaScript when you set a new variable to the value of an object variable then both variables will point to the same object:

var a = [{a:0}];
var b = a
a.splice(0,1);
console.log(b);//=[] empty array

What you're looking for is making a clone of your object, I could find goog.object.clone and goog.object.unsafeClone in the closure library.

clone will only do a shallow copy so if your array would contain immutable objects or primative types you can use clone but if you are going to do something with the cloned array as in the following example you'll need unsafeClone:

var a = [{a:0}];
var b = goog.object.clone(a);
a[0].a=22;
console.log(b[0]);//=22
// b isn't even an array here so I'm not sure what clone does
//   but it doesn't do much
console.log(b instanceof Array);//false

If you're never going to change the values of the array's items and never call functions that'll change the items internal values (like copiedarray[0].changevalue(); or copiedarray[index].someval=newval;) then you can copy a reference to the items in a new array:

var a = [{a:0}];
var b = a.concat([]);
// can't do a[0].a=newValue or it'll permanently change
a.splice(0,1);
console.log(b[0]);//=0

UnsafeClone will fully clone the object but you'll run the risk of starting a never ending loop when a property of any object (property) references itself:

var a = [{a:0}];
var b = goog.object.unsafeClone(a);
a[0].a=22;
console.log(b[0]);//=0
console.log(b instanceof Array);//true

In your case; since the object to be copied comes from a JSON string there won't be circular references but there may be a better way to do it since unsafeClone may be slower than goog.json.parse

...
if (request.isSuccess()) {
    targetVocab = request.getResponseText();
    setVocab();
}
...
var setVocab = function() {
  vocab = goog.json.parse(targetVocab)['targetVocab'];
}    
...
HMR
  • 30,349
  • 16
  • 67
  • 136