How to extract data from this object? - javascript

I am working on some legacy code for my workplace and cannot figure out how to process data from a data object that is returned. The retrieveThis function is supposed to retrieve the object data:
myObj.retrieveThis(new myObj.getThisData({num : 10, page : 1, sorting : "stampDescending"}), function () {myCallback(this);});
var myObj = function () {
var getThisData = {
// this function populates an array and returns it to the retrieveThis function
}
var theObject = {
retrieveThis: function(a, b) {
var OBJ = {};
// OBJ is populated by the data from var getThisData...I checked
setTimeout(function () {
b(OBJ);
}, 1000);
}
}
return theObject;
})();
I am having trouble getting the data ("theObject") to my callback function (or at all). I pass this to myCallback(), where myCallback is:
function myCallback(obj) {
alert(Object.keys(obj));
}
The alert box shows a list of keys including document, jQuery, and myObj. It looks like the data from OBJ is populated from the array allTheData, but I can't seem to pass this back (as return theObject;) to process it. Where am I going wrong here?
Note - I cannot modify this legacy code. I need to process "theObject".

You pass wrong callback function
inside you call it with parameters, so you should define it with parameters
myObj.retrieveThis(
new myObj.getThisData({num : 10, page : 1, sorting : "stampDescending"}),
myCallback);
in this case in first param to myCallback passed OBJ object

Related

unable to get value that is set in a callback

I'm having an issue getting a value that is set in a callback. I initially make a call to get Quest data, then call game.state.setNPCs after the quest data has returned.
I want to get the NPC object after it has been set, but the get is returning an empty array even though setNPCs() seems to be setting the array.
You can see below, after the callback to set the result.npcs, I log out getNPCs(), and it is an Empty Array.
Even more weird, I call game.state.getNPCs() from within the GameState object after the value has been set, but it is still an empty array.
EDIT: I find if I pass in game.state.getNPCs as a callback into the initial callback setNPCs(), like so:
callback(result.npcs, game.state.getNPCs);
Then this works... But I don't want to have to pass in another callback. See below.
Initial call with game.state.setNPCs callback:
Utilities.game.quest.getQuestData({ id : stat.quest_id }, game.state.setNPCs);
Call to getQuestData:
getQuestData : function (params, setNPCcallback) {
API.Quest.getQuestData(params).done(function (result) {
if (game.state) {
game.state.setQuest(result); //Object received successfully
setNPCcallback(result.npcs, game.state.getNPCs);
console.log('NPCs', game.state.getNPCs()); //Empty array
}
});
},
GameState object:
var GameState = function(args) {
this.npcs = [];
...
};
GameState.prototype = {
constructor : GameState,
getNPCs : function () {
console.log(this.npcs); //Empty array
return this.npcs;
},
setNPCs : function (npcsArray, getNPCcallback) {
this.npcs = npcsArray;
console.log(this.npcs); //Contains Object
console.log(game.state.getNPCs()); //Empty array
console.log(getNPCcallback()); //Contains Object
},
I made a small demo to test the issue.
var Person = function () {
this.name = "someone";
}
Person.prototype.walk = function () {
console.log(this)
};
var p = new Person;
function exec(callback) {
callback(); //context is Window
callback.call(p); //context is Person {name: "someone"}
}
p.walk(); //context is Person {name: "someone"}
exec(p.walk);
When you invoke the callback from getQuestData, the context is not GameState instance. Invoking the callback with the correct context using call or apply methods, or using a callback which is bound to correct context using bind method should fix the issue.
Else you can pass the GameState instance itself and invoke gameState.callbackMethod()
which should look like the following according to previous example
function exec(instance) {
instance.walk(); //context is Person {name: "someone"}
}
exec(p);

Passing data to a function as a key and value

I have been trying to execute a function as follows, but am not succeeding.
var leagueSelect = "allLeagues";
loadTable("#loadLeaguesTable","php/leagueTable.php",'leagueSelect',leagueSelect);
The loadTable function:
function loadTable(tableDiv, tableURL, tableType, tableVal){
$.post(tableURL,{tableType:tableVal},function(data){
$(tableDiv).append(data);
});
}
I'm trying to send the values in the post in this format: { leagueSelect: leagueSelect }.
If I hard-code leagueSelect into my function in place of the tableType parameter in the $.post function, it works.
How am I supposed to send 'leagueSelect' properly in my function call?
I'm thinking the data[key] = value method but I didn't get that working either.
Thanks.
When you assign the variable you're actually creating a new object with the property tableType set to whatever the tableVal value is.
Instead you need to use the object[key] = val; syntax or pass the post data in directly like this:
var leagueSelect = "allLeagues";
loadTable("#loadLeaguesTable", "php/leagueTable.php", { leagueSelect: leagueSelect });
function loadTable(tableDiv, tableURL, postData) {
$.post(tableURL, postData, function(data) {
$(tableDiv).append(data);
});
}

Race Condition of Backbone.Collection fetch

I'm generating date list in a selected month where for every date, I will make a Backbone.Collection fetch.
Here is how I loop for every single date in a month
for (var i = 1; i <= numOfDays; i++) {
var d = i < 10 ? '0'+i:i;
var v = new View({
dt: this.year + this.month + (d),
param: this.array
});
this.$el.append(v.render().el);
}
As you can see above, each view (backbone.view) will represent a date and param object. Then I loop the param object using UnderscoreJs _.each method upon callin render()
_.each(this.param, this.reading, this);
and later it will initiate a new object of backbone.collection and perform fetch right away.
reading: function (value, key) {
var _this = this;
this.fetchData(new Data.Collection(), '/api/ + value.ipid').done(function(coll) {
var input = $('<input>').val(value.ipid).attr('data-inid', coll.first().get('in_id'));
_this.$el.append(input);
});
}
I separate a function this.fetchData like this
fetchData: function (obj, url) {
var deferred = $.Deferred(),
collection = obj;
collection.url = url;
var xhr = collection.fetch().done(function(data, textStatus, jqXHR) {
deferred.resolve(collection, data, textStatus, jqXHR);
}).fail(deferred.reject);
var promise = deferred.promise();
promise.abort = _.bind(xhr.abort, xhr);
return promise;
}
Unfortunately, for each view, the order of item inside param object will shift because it depends on race condition of backbone.ajax. Let see the items of param object
[{ipid: 44, measure: "cumec"},{ipid: 45, measure: "meter"},{ipid: 46, measure: "milimeter"}{ipid: 47, measure: "cumec"}]
The object items are in proper order. 44, 45, 46 and 47. But to get the listing as we pass it initially will change after fetch operation.
How do I tell backbonejs or underscorejs to wait every fetch operation to complete before start looping another item inside param object
I just want the loop (_.each) to wait for the fetch operation to complete before it continue looping
Hope somebody can enlighten the way to achieve this. Thank you and have a good day
If you create and append the DOM element at view creation time (i.e. inside the reading function), then reference from inside the done callback, the elements will be in the same order as inside your param array.
Side note: You can instantiate the collection with the url directly, and there is no need to wrap another jQuery promise around the promise returned by fetch.
I think the solution to your problem is using the iterator provided by Async.js library, so your code would look like:
async.eachSeries(this.param, _.bind(this.reading, this), yourCallbackWhenTheArrayIsFinished);
reading: function (value, callback) {
var _this = this;
this.fetchData(new Data.Collection(), '/api/ + value.ipid').done(function(coll) {
var input = $('<input>').val(value.ipid).attr('data-inid', coll.first().get('in_id'));
_this.$el.append(input);
callback();
});
}
So until callback() is called from the done handler, eachSeries doesn't process the next element

Backbone.js requestPager conditionally exclude paramater from URL

RequestPager sends all the attributes in server_api to the request as query string. However, sometime I want to exclude a parameter on some condition. This is how, i'm setting the param:
server_api: {
query: function () {
return this.searchQuery
},
type: function(){ return this.searchType }
}
If this.searchQuery is empty, it makes the URL like ?query=&type=1. But I don't want to send query or type when it's empty or when my some other condition fails.
I know the dirty way like:
if(!myCollection.searchQuery){
delete(myCollection.server_api.licensed);
}
But this is not maintainable. Because text time I've to create this function. So, I'm looking for a better way of doing this. Any Help?
If you look at how server_api is used:
_.each(_.result(self, "server_api"), function(value, key){
if( _.isFunction(value) ) {
value = _.bind(value, self);
value = value();
}
queryAttributes[key] = value;
});
you'll see that it uses _.result:
result _.result(object, property)
If the value of the named property is a function then invoke it;
otherwise, return it.
var object = {cheese: 'crumpets', stuff: function(){ return 'nonsense'; }};
_.result(object, 'cheese');
=> "crumpets"
_.result(object, 'stuff');
=> "nonsense"
That means that you can make server_api a function which returns the appropriate object.

Trouble referencing variable in Collections.where method within render function

I have run into some trouble with a piece of backbone code. The code below relates to a render function. I can retrieve all the models. My trouble arises when I try to use the "Collections.where" method at line marked number #1. As you can see, I have passed an object literal into the render function but for some reason I am unable to reference it within the customers.where method on line #1. When I give this method a literal number like 45 it works. Is there some way around this so I can pass the variable reference in?
Thanks alot
render: function(options) {
var that = this;
if (options.id) {
var customers = new Customers();
customers.fetch({
success: function (customers) {
/* #1 --> */ var musketeers = customers.where({musketeerId: options.id});
console.log(musketeers.length) //doesn't work as options.id is failing on last line
var template = _.template($('#customer-list-template').html(), {
customers: customers.models
});
that.$el.html(template);
console.log(customers.models);
}
});
} else {
var template = _.template($('#customer-list-template').html(), {});
that.$el.html(template);
}
}
Although it isn't explicitly documented, Collection#where uses strict equality (===) when searching. From the fine source code:
where: function(attrs, first) {
if (_.isEmpty(attrs)) return first ? void 0 : [];
return this[first ? 'find' : 'filter'](function(model) {
for (var key in attrs) {
if (attrs[key] !== model.get(key)) return false;
}
return true;
});
},
note the attrs[key] !== model.get(key) inside the callback function, that won't consider 10 (a probable id value) and '10' (a probable search value extracted from an <input>) to be a match. That means that:
customers.where({musketeerId: 10});
might find something whereas:
customers.where({musketeerId: '10'});
won't.
You can get around this sort of thing with parseInt:
// Way off where you extract values from the `<input>`...
options.id = parseInt($input.val(), 10);

Categories