block functions from JS objects in ajax call - javascript

I am having a bit of an issue that I have never seen before. Usually when I build JS "classes" I will just do the simple object style ...
var page = {
add: function(k, v) {
this[k] = v;
}
}
I recently tried doing it with the var page = function() { blah blah } page.prototype = { style but now when I am trying to send off stuff through ajax everything breaks. I put in console logs to try to see what is happening and it seems that in the ajax call, the objects are being turned into window scope, and then the functions are trying to be parameterized. Horrible explanation, I apologize, here is the code, maybe it will make more sense...
var bioLocation = function(obj) {
this.ID = 0,
this.name = '',
this.address = '';
if(typeof obj != 'undefined') {
return this.init(obj);
}
}
bioLocation.prototype = {
init: function(obj) {
for(var index in obj) {
this[index] = obj[index];
}
}
}
Obviously a stripped down version of my real object, but a perfect representation. So then later on in the code I have a button even that will send data off over ajax to save some stuff like so.
$('.save').click(function() {
var postData = {};
var saveTest = new bioLocation({ID: $('.whatever').val()});
postData.locations = saveTest;
var reply;
$.ajax({
async: false,
type: "POST",
data: postData,
dataType: 'json',
success: function(msg) {
reply = msg;
}
});
});
Again, obviously a very simplified version of my code, but pretty much right on target.
The problem is I am getting very strange errors...
"cannot read property 'undefined' of undefined" the line will point directly to the second line of the init function in the bioLocations prototype.
I have done tons of console logs throughout the code, when I put them in the init method it will log twice, once when I create the object in the click function, and once in the ajax call. Here is the code with the console logs in it...
var bioLocation = function(obj) {
this.ID = 0,
this.name = '',
this.address = '';
if(typeof obj != 'undefined') {
return this.init(obj);
}
}
bioLocation.prototype = {
init: function(obj) {
console.log(this);
for(var index in obj) {
this[index] = obj[index];
}
}
}
So I will get 2 console logs when I click the submit button, the first shows...
bioLocation {ID: 5, name: '', address: ''}
then the second time it logs (still within the same click event) it will log...
Window {top: Window, window: Window, location: Location, external: Object...}
and then I will get that error "Cannot read property 'undefined' of undefined"
I can't figure out why it is logging it twice, and more so, why is the second log a Window object. It seems like the ajax call is trying to serialize the functions and getting errors, but I have never seen a prototype being serialized when the object is passed into ajax as a param. I can confirm that it is in fact trying to param the functions because with more logging and hair pulling I was actually able to see the ajax call in the network tab and it did in fact have the init: undefined as one of the params being passed to the ajax page.
So I guess my questions are, how do you stop jquery's ajax function from parameterizing the prototype piece of an object when using it as a param? And, why on earth is it actually adding the prototype into the the call?
Please, you gotta help me, I am loosing my mind here. I have never seen anything like this. Thank you in advance.

The constructor needs to return the new object, not the return value of the init function. Omit the return and it will return this by default. You also had commas where you should have had semi-colons. By convention, classes are capitalized.
var BioLocation = function (o) {
this.ID = 0;
this.name = '';
this.address = '';
if (typeof o !== 'undefined') {
this.init(o);
}
}
BioLocation.prototype = {
init: function (o) {
for (var i in o) {
this[i] = o[i];
}
}
};
var saveTest = new BioLocation({ ID: 5 });
console.log(saveTest);

Related

Avoiding multiple load of javascript file

I add this snippet to each javascript file used in my asp.net web api application to avoid multiple load :
Fullcalendar.js
blog = {};
blog.comments = blog.comments || {};
blog.comments.debugMode = false;
blog.isFirstLoad = function (namesp, jsFile) {
var isFirst = namesp.jsFile.firstLoad === undefined;
namesp.jsFile.firstLoad = false;
return isFirst;
};
$(document).ready(function () {
if (!blog.isFirstLoad(blog.comments, "fullcalendar.js")) {
return;
}
});
Sometimes I get a weird exception
Uncaught TypeError: Cannot read property 'firstLoad' of undefined
I need to know :
Why this happens?
How can I fix it?
A couple of problems there.
First, you shouldn't be loading the file more than once in the first place, so it shouldn't be necessary to go through this business of trying to figure out whether you've loaded it.
But if you want to do that:
The first practical issue is that you're always doing this:
blog = {};
...which means if there's already a blog global, you're wiping out its value and replacing it with an empty object. If you want to use an existing global's value or create a new one, do this:
var blog = blog || {};
That seems odd, but since repeated var declarations are fine (and don't change the variable), that will use an existing one's value, or if there isn't one (or its value is falsey) it will create a new one and initialize it with {}.
Then, the line
namesp.jsFile.firstLoad = false;
...looks for a property called jsFile on namesp and assumes it's not null or undefined. It doesn't look for a property using the jsFile argument's value.
To do that, use brackets notation:
namesp[jsFile].firstLoad = false;
Even then, though, you're assuming it's not null or undefined, but it may well be. You probably just wanted:
namesp[jsFile] = false;
Or possibly:
namesp[jsFile] = namesp[jsFile] ||{};
namesp[jsFile].firstLoad = false;
That said, it seems really odd to use blog.comments to track whether JavaScript files have been loaded. If the file may have already been loaded, just this will do it:
var fullCalendarLoaded;
if (fullCalendarLoaded) {
// It's already loaded
} else {
// It isn't, but it is now
fullCalendarLoaded = true;
// ...do your init...
}
Or if you have several of these and want to use a single global for it:
var loadedScripts = loadedScripts || {};
if (loadedScripts.fullCalendar) {
// Already loaded
} else {
// Not loaded yet
loadedScripts.fullCalendar = true;
// ...do init...
}
Or if using the filename is important:
var loadedScripts = loadedScripts || {};
function firstLoad(filename) {
if (loadedScripts[filename[) {
return false;
}
// Not loaded yet, remember we've loaded it now
loadedScripts[filename] = true;
return true;
}
Then:
if (firstLoad("fullcalendar.js")) {
// First load, do init...
}
It's fairly straightforward:
On your initial run, you define
blog = {};
blog.comments = blog.comments || {};
blog.comments.debugMode = false;
In theory, this means that on some loads, blog is:
var blog = {
comments: {
debugMode: false
}
}
You then pass blog.comments into your function isFirstLoad as the namesp parameter. In that function, you do the evaluation:
namesp.jsFile.firstLoad === undefined;
Well, you never defined the jsFile property of blog.comments. This means it is undefined. Trying to access the property firstLoad of an undefined variable will give you your error
Uncaught TypeError: Cannot read property 'firstLoad' of undefined

JS Object odd behavior

I am currently building a attempting to build a Multiplayer game with Phaser and Eureca io. I am at the stage where i am trying to nail the authentication of the players and what they are controlling, i am doing this by on the server having a method that returns the correct player id.
The method on the server is -
eurecaServer.exports.getPlayer2Id = function(name , id)
{
if(name == "Fly" && id == player2id)
{
console.log("correct fly player");
return player2id;
}
else
{
console.log("Wrong fly player");
return id;
}
}
On the client side at the moment i am only testing, but when i call the function it returns an object. When simply looking at the object in the console it displays this.
Object {status: 0, result: null, error: null, sig: "eY0IjunQt7"}
so it is telling me my result is null which is odd, but when expanding it i get.
Object {status: 0, result: null, error: null, sig: "eY0IjunQt7"}
callback: function()
error: null
errorCallback: function()
onReady: (fn, errorFn)
result: "jVcZvzetc8AAK45NAAAH"
sig: "eY0IjunQt7"
As you can see when expanding the result is not null, and is exactly what im looking for, i have tried, JSON.stringify but it gives me the first result, where it tells me the result is null.
Any ideas how i can get my actual result or why this is happening.
Thanks.
Edit:
The client side component for server is defined in the main game file
var eurecaClientSetup = function() {
var eurecaClient = new Eureca.Client();
eurecaClient.ready(function (proxy) {
eurecaServer = proxy;
});
Then in the object class it is called here.
this.name = "Fly"
this.id = index; //Passed in on object creation
this.currPlayer = eurecaServer.getPlayer2Id(this.name, this.id);
console.log(JSON.stringify(this.currPlayer));
console.log(this.currPlayer);
Any server operation is going to be asynchronous, regardless of the framework*. Different frameworks have different ways of getting the result once it is available. In Eureca.io, it seems to be using an .onReady() call after the server function name.
In other words, what you need to do is change your client code to the following:
this.name = "Fly"
this.id = index; //Passed in on object creation
var _this = this; // needed because "this" inside function below won't be the same
eurecaServer.getPlayer2Id(this.name, this.id).onReady(function (id) {
_this.currPlayer = id;
console.log(_this.currPlayer); // shows the right thing
// do other stuff here now you have the right player id
});
* Technically, you can do a synchronous/blocking AJAX request, but it is bad practice in 99% of cases.
I'm the author of eureca.io, the answer given by GregL is correct.
I just want to add that onReady() call will be deprecated, eureca.io support promise like call (using then() function).
so you can use this syntax instead, which is more standard.
this.name = "Fly"
this.id = index; //Passed in on object creation
var _this = this; // needed because "this" inside function below won't be the same
eurecaServer.getPlayer2Id(this.name, this.id).then(function (id) {
_this.currPlayer = id;
console.log(_this.currPlayer); // shows the right thing
// do other stuff here now you have the right player id
});

How am I getting the scope wrong? Unable to access this function from jQuery ".on"

Morning all,
I'm using the following code to somewhat imitate setInterval with AJAX:
// Poll for ALERTs
(function pollForAlerts() {
var params = { "send": 1, "poll": 1 };
// Set up the correct patch for sending AJAX data
ALERTS = {};
ALERTS.Auth = { site: data_site, uuid: data_uuid };
ALERTS.API = function(app,data) {
var url = "//myurl.com/alerts/"+ app +"/?";
var data = $.extend({}, ALERTS.Auth, data);
return url + jQuery.param(data || "") + '&timestamp='+$.now();
}
// Run the AJAX request
$.getJSON( ALERTS.API( 'touchscreen', params ) , function (response) {
if( typeof response === "object" ) {
for( var i = 0; i < response.length; i++ )
renderAlert(response[i]);
} else { setTimeout( pollForAlerts, 3000 ); }
});
}());
The function runs repeatedly until it finds a response.
I'd like to then set a jQuery ".on" to restart this loop if a certain element is clicked on:
// Respond to ALERT
$('#alerts').on('click', 'td.initials span', function(event) {
$(this).closest('tr').removeClass("active abs cri");
pollForAlerts();
});
However, when I do that, I get the following error in Firebug:
ReferenceError: pollForAlerts is not defined
http://myurl.com/alerts/static/js/touchscreen.js
Line 14
I can't work out why pollForAlerts() can't be accessed. Is it because of the self-executing function, or is it just because it's being used within jQuery's on function?
I'm not JavaScript expert, especially when it comes to self-executing functions and closures, so please be gentle with me!
Duncan
You wrote self invoking function, These functions are executing only once in lifetime. If you want to call a function multiple times then you can write it as normal function.

SPA and Knockout.js AddViewModel breakdown

I am having some issues trying to work out what is going ok with MVC SPA and Knockout.
When you create a new project some files are created for knockout.js as examples, but I am struggling to understand what is going on.
Primarily the issue is with the app.viewmodel.js and the function AddViewModel.
Here is some code which I will attempt to breakdown:
self.addViewModel = function (options) {
var viewItem = {},
navigator;
// Example options
//{
// name: "Home",
// bindingMemberName: "home",
// factory: HomeViewModel
//}
// Add view to AppViewModel.Views enum (for example, app.Views.Home).
self.Views[options.name] = viewItem; // Don't really get this, seems to add a blank object to app.Views.Home
// Add binding member to AppViewModel (for example, app.home);
self[options.bindingMemberName] = ko.computed(function () {
//if (self.view() !== viewItem) {
// console.log(self.view()); // returns {}
// console.log(viewItem); // returns {}
// return null; // should never hit this?
//}
return new options.factory(self, dataModel); // This adds our ViewModel to app.home, app.login, etc
});
// This checks to see if we have defined a navigatorFactory in our viewmodel (AddViewModel)
if (typeof (options.navigatorFactory) !== "undefined") {
navigator = options.navigatorFactory(self, dataModel);
} else {
navigator = function () {
console.log(viewItem);
self.view(viewItem);
};
}
// Add navigation member to AppViewModel (for example, app.NavigateToHome());
self["navigateTo" + options.name] = navigator;
};
ok, so let's start. First of all we declare 2 variables:
var viewItem = {},
navigator;
viewItem is set as a blank object and navigator is undefined.
The first thing we do, is set self.Views[options.name] to our viewItem, so in my understanding, this would mean:
self.Views.Home = {}
If we look at the declaration in app.viewmodel.js self.Views looks like this:
self.Views = {
Loading: {} // Other views are added dynamically by app.addViewModel(...).
};
So in here there is already a view called Loading. So I am confused as to what is actually happening here.
The next bit of code creates a function:
self[options.bindingMemberName] = ko.computed(function () {
return new options.factory(self, dataModel);
});
This is a lot easier to understand. It basically takes our ViewModel and adds it to a function under the name of self.home (or whatever the bindingMemberName of our ViewModel is.
This next piece is what confuses me:
if (typeof (options.navigatorFactory) !== "undefined") {
navigator = options.navigatorFactory(self, dataModel);
} else {
navigator = function () {
console.log(viewItem);
self.view(viewItem);
};
}
// Add navigation member to AppViewModel (for example, app.NavigateToHome());
self["navigateTo" + options.name] = navigator;
If I strip this down, it basically says if we define a navigatorFactory, then the navigator (which is currently undefined!) is equal to our navigatorFactory. That bit is easy.
It's the next bit I don't get.
It says, else, the navigator is a function that returns our self.view(viewItem) (remember that viewItem is just a blank object.
Then we set self["navigateTo" + options.name] = navigator.
So in english, this looks like it is saying, get our blank viewItem, assign it to self.view for every ViewModel we add. Then assign a function returning our self.view(viewItem) to our navigator variable (which is currently undefined) and assign this to our self.naviateToHome() (or whatever).
So to me, that looks like self.navigateToHome(), self.navigateToLogin(), self.navigateToTimbucktoo() would all return the same function with the same self.view.
So, can anyone explain to me what is actually happening?
Update 1
So, I have figured some things out. First things first, the navigator is setting the current view, so basically self.Views looks like this after all the models are added:
self.Views = {
Loading: { },
Home: { },
Login: { }
}
So even though self.view() returns an empty object, it isn't the same as the viewItem because it is stored with the name into self.Views.
So, the navigator is actually applying the viewItem to self.views.
I tested this out by changing the viewItem to this:
var viewItem = { options.name }
and sure enough, self.Views looked liked this:
self.Views = {
Loading: { },
Home: { name: "Home" },
Login: { name: "Login" }
}
so when we set self.view using our navigator, the function is called (app.home for example) and it runs the code to return our factory or null if it isn't the current view.

Custom JQuery Plugin Method error

I've been working on writing a custom jquery plugin for one of my web applications but I've been running into a strange error, I think it's due to my unfamiliarity with object-oriented programming.
The bug that I've been running into comes when I try to run the $(".list-group").updateList('template', 'some template') twice, the first time it works just fine, but the second time I run the same command, I get an object is not a function error. Here's the plugin code:
(function($){
defaultOptions = {
defaultId: 'selective_update_',
listSelector: 'li'
};
function UpdateList(item, options) {
this.options = $.extend(defaultOptions, options);
this.item = $(item);
this.init();
console.log(this.options);
}
UpdateList.prototype = {
init: function() {
console.log('initiation');
},
template: function(template) {
// this line is where the errors come
this.template = template;
},
update: function(newArray) {
//update code is here
// I can run this multiple times in a row without it breaking
}
}
// jQuery plugin interface
$.fn.updateList = function(opt) {
// slice arguments to leave only arguments after function name
var args = Array.prototype.slice.call(arguments, 1);
return this.each(function() {
var item = $(this), instance = item.data('UpdateList');
if(!instance) {
// create plugin instance and save it in data
item.data('UpdateList', new UpdateList(this, opt));
} else {
// if instance already created call method
if(typeof opt === 'string') {
instance[opt](args);
}
}
});
}
}(jQuery));
One thing I did notice when I went to access this.template - It was in an array so I had to call this.template[0] to get the string...I don't know why it's doing that, but I suspect it has to do with the error I'm getting. Maybe it can assign the string the first time, but not the next? Any help would be appreciated!
Thanks :)
this.template = template
Is in fact your problem, as you are overwriting the function that is set on the instance. You end up overwriting it to your args array as you pass that as your argument to the initial template function. It basically will do this:
this.template = ["some template"];
Thus the next time instance[opt](args) runs it will try to execute that array as if it were a function and hence get the not a function error.
JSFiddle

Categories

Resources