Assigning scope amongst jQuery.getJSON and a JS.Class - javascript

I'm trying to assign some JSON data to a property of a JS.Class instance.
var MyClass = new JS.Class({
initialize: function(uuid) {
this.uuid = uuid;
},
write: function() {
$.getJSON(url+"?callback=?", {}, function(data) {
Assign(data);
});
function Assign(data) { this.content = data; };
}
});
var m = new MyClass("uuid_goes_here");
m.write();
The JSON is received asynchronously, which is why there's a function call within the $.getJSON callback.
The problem I have now is that the this.content within the Assign function is not within the scope of the instance method named write. So whereas this.uuid returns correctly, this.content remains undefined (as you would expect).
Any ideas on how to correct this? I've tried using a global variable as a workaround but the async call doesn't allow for that (plus it's a crappy solution).
Some points to note, in case they matter: I have to use JSONP, so the "?callback=?" has to stay, and I'd like to keep it async.

I would usually go for either czarchaic's version, or replace Accept with a bound method from the object. What you have to bear in mind is that calling Accept() like that (as a function call rather than a method call) will bind this to the global object, i.e. window. I'd try this:
var MyClass = new JS.Class({
initialize: function(uuid) {
this.uuid = uuid;
},
write: function() {
$.getJSON(url+"?callback=?", {}, this.method('setContent'));
},
setContent: function(data) {
this.content = data;
}
});
See http://jsclass.jcoglan.com/binding.html for more info.

You should cache the current instance in the write method and update it after ajax.
write: function() {
var self=this;
$.getJSON(url+"?callback=?", {}, function(data) {
self.data=data;
});
}

Related

Javascript problems with bind

I'm new to JS, and have a problem figuring out why bind makes me problems:
Here is a snippet:
var MyTest = function() {
registerChannel(address, null, this._messageHandler.bind(this));
};
MyTest.prototype._messageHandler = function(msg) {
this._logToConsole(msg);
};
MyTest.prototype._close = function() {
(function(inst) {
asyncSend(address, data,
function(err, reply) {
unregisterChannel(address, inst._messageHandler.bind(inst));
}
);
})(this);
}
The registerChannel method stores the callback function I passed and the unregisterChannel tries to verify whether this callback exists and fails because 'bind' copies the method and doesn't pass them as reference (to the best of my knowledge).
The result is that unregisterChannel fails because it can't find the function I passed.
How can I solve this? apply\call ? not familiar with those...
Thank you and I hope my question did sense...
Each time you call .bind you get a new reference, so the "register" and "unregister" phases won't match because you're not passing exactly the same reference.
Therefore each object will need to retain its own copy of its bound handler, e.g.:
var MyTest = function() {
this._handler = this._messageHandler.bind(this);
registerChannel(address, null, this._handler);
};
and you can then pass this._handler to your unregisterChannel call.
An alternative would be to not put _messageHandler on the prototype at all, and create a new instance within the constructor:
var MyTest = function() {
this._messageHandler = (function(msg) {
...
}).bind(this);
registerChannel(address, null, this._messageHandler);
};

Knockout Mapping Object Not Returned by Durandal Viewmodel

I am planning to use the Knockout Mapping plugin to map data returned from ajax calls to the viewmodel under the Durandal framework. However, I did not know how to return the mapped object so that the view can use it. Here's my code for the viewmodel login.js:
define(function (require) {
var system = require('durandal/system'),
app = require('durandal/app'),
ko = require('knockout'),
komapping = require('komapping'),
deferred = $.Deferred(),
loginOptionsUrl = '/json/loginOptions.json',
loginInterfaceData = {};
ko.mapping = komapping;
return {
activate: function(){
var request = $.getJSON(loginOptionsUrl)
.done(function(data){
loginInterfaceData = ko.mapping.fromJS(data);
system.log(loginInterfaceData); //Do a check here; loginInterfaceData contains all the right data
deferred.resolve();
});
return deferred.promise();
},
loginInterfaceData: loginInterfaceData; //However, the view gets an empty object
As you can see, the object returned from ko.mapping is assigned to loginInterfaceData, which I return to the view. However, when I examine the loginInterfaceData object returned by 'viewmodels/login' under the Web Inspector, it is an empty object. The only reasoning I can come up with is that the viewmodel returns loginInterfaceData before the mapping is finished. However, I have no idea how to prevent that from happening.
Any ideas/advice will be most appreciated. Thanks in advance!
This happens because you return a reference to inner loginInterfaceData, but in your activate function you reassign that variable, so previously returned loginInterfaceData will always be an empty object. What you can do is return a getter function for your loginInterfaceData like this:
...
getLoginInterfaceData: function() {
return loginInterfaceData;
}
...
And in your calling code, after activate is done, just call getLoginInterfaceData().
Here is simple example:
function foo() {
var bar = {};
return {
setBar : function(x) {
bar = x;
},
bar: bar,
getBar: function() {
return bar;
}
}
}
var f = foo();
f.setBar(5);
console.log(f.bar); // logs empty object
console.log(f.getBar()); // logs value of inner foo.bar
Apparently the problem lies in returning the loginInterfaceData object before the activate function is invoked, causing the view to receive the original empty object. My solution ends up being to bind loginInterfaceData to empty data first, then updating it once the data has been received via Ajax. Like this:
(...)
ko.mapping = komapping;
loginInterfaceData = ko.mapping.fromJS({}); //instead of assigning loginInterfaceData = {}, I use KO Mapping with an ampty JSON object
(...)
return {
activate: function(){
return $.ajax({
url: loginOptionsUrl,
contentType: 'application/json',
}).done(function(data) {
ko.mapping.fromJS(data, loginInterfaceData);//so here, instead of mapping for the first time, I remap/update it
});
},
loginInterfaceData: loginInterfaceData; //at this point, loginInterfaceData is still empty object but will be updated/remap once ajax call is successful
Hope this helps someone who runs into the same issue as I did. Cheers.

accessing object properties with object method

I am trying to build an application called myApp which has the property regularNameErrors and the Method populateJSON which uses an AJAX call to get a JSON object which is then added to the property declared as one of the arguments to the method.
The function to get the data definitely works because the alert at position 3 gives the correct list of keys. however the alert at 1 returns undefined and the alert at 2 blank.
When I replace the line destination = data with myApp.regularNameErrors = data again it works so I assume that I have misunderstood how to pass object properties to object methods. However I want to use the method populateJSON several times and need to know the correct way to pass it properties.
var myApp = {
init: function () {
myApp.populateJSON(myApp.regularNameErrors, 'data/spelling.json').done(function () {
alert(myApp.regularNameErrors['kingscross']); //1
});
},
regularNameErrors: {}, //Object of spelling mistake:proper key:value pairs
populateJSON: function (destination, source) {
var def = $.Deferred();
$.getJSON(source, function (data) {
destination = data;
alert(Object.keys(myApp.regularNameErrors)); //2
alert(Object.keys(data)); //3
def.resolve();
});
return def.promise();
},
};
With the ability to use the deferred properties of the returned getJSON object the functions can be reduced to a few lines such that it is not necessary to create a new function called populateJSON
var myApp = {
init: function () {
$.getJSON('data/spelling.json').done(function(data) {
myApp.regularNameErrors = data;
});
},
regularNameErrors: {},
};
destination is no "pointer" to the property that you passed into the function, but a variable holding the value which the property evaluated to. When you assign to destination, you just write to the local variable of your populateJSON function.
If you want to be able to declare the destination, you would need to pass a base object (or always use myApp) and a property name:
var myApp = {
init: function () {
myApp.populateJSON('regularNameErrors', 'data/spelling.json', function() {
alert(Object.keys(myApp.regularNameErrors)); //2
alert(myApp.regularNameErrors['kingscross']); //1
});
},
regularNameErrors: {}, //Object of spelling mistake:proper key:value pairs
populateJSON: function (destinationName, source, callback) {
$.getJSON(source, function (data) {
myApp[destinationName] = data;
alert(Object.keys(data)); //3
callback();
});
}
};
However, I see you are using the promise pattern. A promise should not be used as a simple notifier ("now the data has arrived somewhere"), but it should represent the data itself ("now, here's the data") - resolve it with the data instead of storing the data in a global variable/property and resolving with nothing. Actually, the $.ajax does already return such a promise so you don't have to do much for it:
var myApp = {
init: function () {
myApp.regularNameErrors = $.getJSON('data/spelling.json');
myApp.regularNameErrors.done(function(data) {
alert(Object.keys(data)); //3
alert(data['kingscross']); //1
});
}
};

Getter returning the function instead of "return" value

Im trying to use getters and setters for a project in javascript, I'm getting a json from a jquery get and im setting the value with a setter, I can alert the content in the setter function, so I know that everything is okay, but when I try to return a value with a getter, I got the function instead of the value.
My code is
function _bd() {
this.setJson = function(js) {
json = js;
}
this.getJson = function() {
return json;
}
}
bd = new _bd();
$.get("json.php", function(data) {
bd.setJson(data);
},"json");
alert(bd.getJson);
the last alert returns
function () {
return json;
}
also, using prototype does the same result.
I agree with the previous comments. Invoke the getter-function to retrieve the "json" value.
Probably you want to declare the json variable as well (unless you need it globally of some sorts).
Also, you seem to mixing and matching some object construction patterns. The functional pattern, to simulate "private" vars inside a closure (like json), but you also need the prototypical inheritance, so you can attach the getter/setters to this inside the construtor function. Is it possible to stick to one?
E.g.: trapping privates in closure.
function _bd() {
var json;
var that = {};
that.setJson = function(js) {
json = js;
}
that.getJson = function() {
return json;
}
return that;
}
var bd = _bd();
$.get("json.php", function(data) {
bd.setJson(data);
alert(bd.getJson());
},"json");
E.g. OO style, with constructor function.
function BD(){
this._json = null;
}
BD.prototype = {
getJson: function(){
return this._json;
},
setJson: function(json){
this._json = json;
}
};
var bd = new BD();
$.get("json.php", function(data) {
bd.setJson(data);
alert(bd.getJson());
},"json");
There can be good reasons to use a hybrid style, but it helps if you stick with one approach or the other.
As for "real" getters (IMHO, not worth the insane syntax), try:
function BD(){
this._json = null;
}
Object.defineProperty(BD.prototype,"json",{
get: function(){
return this._json;
},
set: function(json){
this._json = json;
}
});
var bd = new BD();
bd.json = {a: "test"};
console.log(bd);
As stated in the comments :
alert( bd.getJson() );
you are getting the string display of the function because you are not invoking the function
before the response come back, you'll get nothing.. probably undefined.
$.getJson
(
success : function() { alert( bd.getJson() ); }
);

Encapsulation in javascript

I need to create simple reusable javascript object publishing several methods and parameterized constructor. After reading through several "OOP in JavaScript" guides I'm sitting here with an empty head. How on the Earth can I do this?
Here my last non-working code:
SomeClass = function(id) {
this._id = id;
}
(function() {
function intFun() {
return this._id;
}
SomeClass.prototype.extFun = function() {
return incFun();
}
})();
This is my usual approach:
MyClass = function(x, y, z) {
// This is the constructor. When you use it with "new MyClass(),"
// then "this" refers to the new object being constructed. So you can
// assign member variables to it.
this.x = x;
...
};
MyClass.prototype = {
doSomething: function() {
// Here we can use the member variable that
// we created in the constructor.
return this.x;
},
somethingElse: function(a) {
}
};
var myObj = new MyClass(1,2,3);
alert(myObj.doSomething()); // this will return the object's "x" member
alert(myObj.x); // this will do the same, by accessing the member directly
Normally the "this" keyword, when used in one of the object's methods, will refer to the object itself. When you use it in the constructor, it will refer to the new object that's being created. So in the above example, both alert statements will display "1".
An exception to this rule is when you pass one of your member functions somewhere else, and then call it. For example,
myDiv.onclick = myObj.doSomething;
In this case, JavaScript ignores the fact that "doSomething" belongs to "myObj". As a result, the "this" inside doSomething will point to another object, so the method won't work as expected. To get around this, you need to specify the object to which "this" should refer. You can do so with JavaScript's "call" function:
myDiv.onclick = function() {
myObj.doSomething.call(myObj);
}
It's weird, but you'll get used to it eventually. The bottom line is that, when passing around methods, you also need to pass around the object that they should be called on.
I usually don't worry too much about hiding the internals, although I do prefix them with underscores to mark them as not intended to be used outside the "class". Normally what I will do is:
var MyClass = function() {};
MyClass.prototype = {
_someVar : null,
_otherVar : null,
initialize: function( optionHash ) {
_someVar = optionsHash["varValue"];
_otherVar = optionsHash["otherValue"];
},
method: function( arg ) {
return _someVar + arg;
},
};
And use it as so...
var myClass = new MyClass( { varValue: -1, otherValue: 10 } );
var foo = myClass.method(6);
All vars are private:
SomeClass = function (id) {
var THIS = this; // unambiguous reference
THIS._id = id;
var intFun = function () { // private
return THIS._id;
}
this.extFun = function () { // public
return intFun();
}
}
Use THIS within private methods since this won't equal what you might expect.
From http://learn.jquery.com/code-organization/concepts/#the-module-pattern:
// The module pattern
var feature = (function() {
// private variables and functions
var privateThing = "secret";
var publicThing = "not secret";
var changePrivateThing = function() {
privateThing = "super secret";
};
var sayPrivateThing = function() {
console.log( privateThing );
changePrivateThing();
};
// public API
return {
publicThing: publicThing,
sayPrivateThing: sayPrivateThing
};
})();
feature.publicThing; // "not secret"
// logs "secret" and changes the value of privateThing
feature.sayPrivateThing();
So using returning an object that aliases its "methods" could be another way to do it.
I've read from http://www.amazon.com/Programming-Oracle-Press-Poornachandra-Sarang-ebook/dp/B0079GI6CW that it is always good practice to use getters and setters rather that accessing the variable directly from outside the object, so that would eliminate the need of returning variables by reference.
BTW you could just use this.variable to reference/declare a public variable and var variable to declare a private variable.
I know this is a late answer, but I hope it helps anyone who reads it in the future.

Categories

Resources