I have a view in Backbone which has multiple functions.
The functions I have are initialize, render, answer, answerQuestion, nextQuestion.
Here is the code I have in the initialize function
initialize: function(game) {
_.bindAll(this, 'render', 'answer');
this.render();
}
In the render function I call the answerQuestion function by doing this:
this.answerQuestion();
It works fine.
But in my answer function I call the nextQuestion function the same way and I get this error undefined is not a function, if I just call the function without the this at the start I get this error 'nextQuestion is not defined'
What am I missing to get this working.
Here is the full answer function:
var v = $('.question.current .type').find('.input').val();
if (v !== undefined) {
var t = new Date();
var time_spent = t.getTime() - this.t.getTime();
var self = this;
answer.save().done(function(result, status) {
if (status === 'success') {
this.nextQuestion();
}
});
}
You're referring to the wrong context with: this.nextQuestion();. It should be self.nextQuestion();. Or you could bind the callback to the external function's context like this:
var v = $('.question.current .type').find('.input').val();
if (v !== undefined) {
var t = new Date();
var time_spent = t.getTime() - this.t.getTime();
var self = this;
answer.save().done(function(result, status) {
if (status === 'success') {
this.nextQuestion();
}
}.bind(this));
}
Related
I am trying to mock out every instance that is created with the new keyword for an object.
Here is the Object I am trying to mock out:
var SharedWhiteboardView = function(moduleEl, domService) {
'use strict';
var self;
var sharedWhiteboardProblemImage;
var whiteboardController;
var caller = false;
var toolbarController;
return {
initWhiteboard : function()
{
self = this;
sharedWhiteboardProblemImage = domService.find(moduleEl, '#sharedWhiteboardModule-sharedWhiteboardProblemImage');
var toolbarEL = $('#sharedWhiteboard-toolbar');
toolbarController = new ToolbarController(WhiteboardConstants.SHARED_WHITEBOARD_ID, toolbarEL, null);
toolbarController.init(false);
whiteboardController = toolbarController.getWhiteboardController();
},
enableWhiteboardEdition : function(enabled)
{
if(self.getWhiteboardObject() && self.getWhiteboardObject.hasOwnProperty('enableEdition')) self.getWhiteboardObject().enableEdition(enabled);
whiteboardController.setEnabled(enabled);
}
};
}
This is the file which I am trying to test and it creates a new instance of the above object
Box.Application.addModule('SharedWhiteboardModule', function(context) {
'use strict';
var self;
var moduleEl;
var domService;
var sharedWhiteboardView;
var modal;
var assignmentTimer = 3000;
var sharing = false;
var assignmentImageData = '';
return {
/**
* Initializes the module and caches the module element
* #returns {void}
*/
init: function() {
self = this;
domService = context.getService('DomService');
moduleEl = context.getElement();
sharedWhiteboardView = new SharedWhiteboardView(moduleEl, domService);
sharedWhiteboardView.initWhiteboard();
sharedWhiteboardView.enableWhiteboardEdition(false);
};
}
I am trying to write a unit test to test that the sharedWhiteboardView.enableWhiteboardEdition method is called with 'false'
However I am failing to attach a spy or stub that method out. I have tried these solutions and they did not work
//First Attempt
sinon.stub(SharedWhiteboardView, "enableWhiteboardEdition", function() {return 0})
// Second Attempt
sinon.stub(SharedWhiteboardView.prototype, "enableWhiteboardEdition").returns(0);
//Third Attempt
sandbox.stub(SharedWhiteboardView.prototype, 'enableWhiteboardEdition', checkEnableWhiteboardEdition());
//Fourth Attempt Trying the answer provided by chrmod
it.only('when type is "SharedWhiteboardModule-setEditable" should call sharedWhiteboardView.enableWhiteboardEdition', function (done) {
const view = SharedWhiteboardView();
sinon.stub(view, "enableWhiteboardEdition", function() {
console.log('Hit');
});
module.onmessage('SharedWhiteboardModule-setEditable', true);
done();
});
No error but it does not hit the console.log, I removed the 'new' keyword as suggested
Errors that I got:
-Attempted to wrap undefined property enableWhiteboardEdition as function
-Cannot stub non-existent own property enableWhiteboardEdition
Please any help would be great. I have reached a dead end here.
Here is a codepen: http://codepen.io/anon/pen/bgmNxx?editors=0011
All I am trying to do is to have the Fake method get hit when my module calls enableEdition
SharedWhiteboardView is not a constructor, it is rather a factory function. Once called (without new) it returns new object that has enableWhiteboardEdition as own property.
Thus a stub has to be set on that object:
const view = SharedWhiteboardView();
sinon.stub(view, "enableWhiteboardEdition", function() {return 0});
This did it.
it('when type is "SharedWhiteboardModule-setEditable" should call setEditable with appropriate callback', function (done) {
var mockSharedWhiteboardView = {
enableWhiteboardEdition: function() {},
initWhiteboard: function() {},
initScrollBar: function() {},
refreshScrollBar: function() {},
isMainWhiteboardAvailable: function() {}
};
sandbox.spy(mockSharedWhiteboardView, 'enableWhiteboardEdition');
var tempGlobals = {
SharedWhiteboardView: global.SharedWhiteboardView
};
global.SharedWhiteboardView = function() {
return mockSharedWhiteboardView;
};
module = Box.Application.getModuleForTest('SharedWhiteboardModule', contextFake);
module.init();
var shouldEnable = true;
module.onmessage('SharedWhiteboardModule-setEditable', shouldEnable);
assert(mockSharedWhiteboardView.enableWhiteboardEdition.calledWithExactly(shouldEnable),
'should enable the whiteboard');
shouldEnable = false;
module.onmessage('SharedWhiteboardModule-setEditable', shouldEnable);
assert(mockSharedWhiteboardView.enableWhiteboardEdition.calledWithExactly(shouldEnable),
'should not enable the whiteboard');
// cleanup
global.SharedWhiteboardView = tempGlobals.SharedWhiteboardView;
done();
});
var Application;
(function (Application, PhotonSdk) {
(function(Photon) {
Photon.PeerManager = (function () {
var $this;
function PeerManager() {
$this = this;
this.currentStatus = PhotonSdk.PhotonPeer.StatusCodes.connectClosed;
this.peer = new PhotonSdk.PhotonPeer("ws://localhost:9090");
this.peer.addPeerStatusListener(PhotonSdk.PhotonPeer.StatusCodes.connecting, this._onConnecting);
this.peer.addPeerStatusListener(PhotonSdk.PhotonPeer.StatusCodes.connect, this._onConnect);
}
PeerManager.prototype.establishConnection = function() {
this.peer.connect();
console.log("Photon is establishing connection.");
};
PeerManager.prototype._onConnecting = function() {
this.currentStatus = PhotonSdk.PhotonPeer.StatusCodes.connecting;
PeerManager.prototype._logConnectionState(this.currentStatus); //It work
};
PeerManager.prototype._onConnect = function () {
this.currentStatus = PhotonSdk.PhotonPeer.StatusCodes.connect;
this._logConnectionState(this.currentStatus); //It isn't work :(
};
PeerManager.prototype._logConnectionState = function (state) {
console.log("Photon connection is " + state + ". " + new Date().toTimeString());
};
return PeerManager;
})();
})(Application.Photon || (Application.Photon = {}));
})(Application || (Application = {}), Photon);
If i use this._logConnectionState(this.currentStatus);
i get this._logConnectionState is not a function error, but
PeerManager.prototype._logConnectionState(this.currentStatus);
or
$this._logConnectionState(this.currentStatus);
is work. Why it's happened and how i can do that access through this well doing?
My suggestion is that events _onConnecting and _onConnect are dispatching by PhotonSdk.PhotonPeer instance. Since you have added listeners here:
this.peer.addPeerStatusListener(PhotonSdk.PhotonPeer.StatusCodes.connecting, this._onConnecting);
this.peer.addPeerStatusListener(PhotonSdk.PhotonPeer.StatusCodes.connect, this._onConnect);
So functions are called with wrong this.
Try this:
function PeerManager() {
$this = this;
this.currentStatus = PhotonSdk.PhotonPeer.StatusCodes.connectClosed;
this.peer = new PhotonSdk.PhotonPeer("ws://localhost:9090");
this.peer.addPeerStatusListener(PhotonSdk.PhotonPeer.StatusCodes.connecting, this._onConnecting.bind(this));
this.peer.addPeerStatusListener(PhotonSdk.PhotonPeer.StatusCodes.connect, this._onConnect.bind(this));
}
PeerManager.prototype._onConnect = function () {
this.currentStatus = PhotonSdk.PhotonPeer.StatusCodes.connect;
this._logConnectionState(this.currentStatus); //It isn't work :(
};
Your reference you used
_logConnectionState(this.currentStatus);
on seems to be this:
PeerManager.prototype._onConnect
and not that:
Peer Manager.prototype
basicly this refers to
PeerManager.prototype._onConnect
and
this._logConnectionState
is the same as
PeerManager.prototype._onConnect._logConnectionState
wich is undefined because there is no local value /function for that reference.
As you see "this" only has a local context always being bound to the first object/function it can find while climbing up the scopes.
I'm now in the process of transforming several functions into prototypes and I'm stuck on callbacks.
Below is a minimal example of what I want to achieve:
WebSocketClient.prototype.send = function(t, data)
{
this.ws.send(data);
this.ws.onmessage = function(evt)
{
var msg = evt.data;
var jsonData = JSON.parse(msg);
if(jsonData["callback"] !== 'undefined' && jsonData["callback"] !== "") // jsonData = {callback:"on_test", data:[0,1,2]}
{
// How to transform callback into call ???
var fn = window[jsonData["callback"]]; // == undefined
if(typeof fn === 'function')
fn(jsonData["data"]);
}
};
};
function Test()
{
this.wc = new WebsocketClient();
// here ws.connect, etc.
}
Test.prototype.send = function()
{
this.wc.send(test, '{request:"get_data", callback:"on_test"')
}
Test.prototype.on_test = function(arr)
{
// ...
}
var test = new Test();
test.send();
I want to make a call to t.callback(data) but can't figure out how to do this?
I tried:
window[jsonData["callback"]]; // == undefined
window['Test.prototype.' + jsonData["callback"]]; // == undefined
window['Test.' + jsonData["callback"]]; // == undefined
There must be an error here:
Test.prototype.send = function()
{
// use 'this' instead of 'test'
// this.wc.send(test, '{request:"get_data", callback:"on_test"')
this.wc.send(this, '{request:"get_data", callback:"on_test"')
}
And since on_test() is defined on Test.prototype, call it this way:
WebSocketClient.prototype.send = function(t, data)
{
this.ws.send(data);
this.ws.onmessage = function(evt)
{
var msg = evt.data;
var jsonData = JSON.parse(msg);
if(jsonData["callback"] !== 'undefined' && jsonData["callback"] !== "") // jsonData = {callback:"on_test", data:[0,1,2]}
{
var fn = t[jsonData["callback"]]; // t will be available in this scope, because you've created a closure
if(typeof fn === 'function') {
fn(jsonData["data"]);
// OR, preserving scope of Test class instance t
fn.call(t, jsonData["data"]);
}
}
};
};
UPDATE: And be wary, that by calling fn(jsonData["data"]); you are loosing the original scope of the method. This way, this inside the on_test() method will point to global scope. If this is undesired, use call() (see corrected above).
If your function is in the global scope you could use
window.call(this, 'functionName', arguments)
In your case,
window.call(this, jsonData['callback'], jsonData['data'])
By doing that, the callback will be invoked with jsonData['data'] as a parameter.
If the function is within an object test, just use
test.call(this, 'on_test', jsonData['data'])
how to correctly make callback in jquery plugin.
(function($) {
var parameter = {
first:'1',
second:'2',
call: $.noop
};
var something = 'yes';
var testf = function(){
// i neeed launch callback here;
var something_else = something + 'no';
alert(something_else)
}
$.fn.sadstory = function(options) {
if (options && typeof options === 'object')
{
$.extend(parameter, options);
}
testf();
return this;
}
})(jQuery);
and i need atccess var and owerwrite or making somthing else with him.
$('elm').sadstory({
call: function(){
this.something = 'no';
}
});
and result would by alert box with text nono instead of yesno, now to make this callback correctly.
i think you can do it like that:
$.fn.sadstory = function(options,callback) {
if (options && typeof options === 'object')
{
$.extend(parameter, options);
}
testf();
// example, var c is passed to callback function
var c= "abc";
callback(c);
return this;
}
you can call like
.sadstory({..},function(c) {
console.log(c) // logs "abc"
})
should also work as property of options
this.something doesn't exist. The only something is a variable with the scope of your testf method.
A solution is to pass an object as a parameter to the callback, and allow the callback to modify this object.
(function($) {
var parameter = {
first:'1',
second:'2',
call: $.noop
};
var something = 'yes';
var testf = function(){
// Initialize the string to a default value
var stringGenerationParams = { something: 'yes' };
// Allow the callback to modify the string generation params
parameter.call(stringGenerationParams);
// At this point, stringGenerationParams.something may have been
// modified by the callback function
var something_else = stringGenerationParams.something + 'no';
alert(something_else)
}
$.fn.sadstory = function(options) {
if (options && typeof options === 'object')
{
$.extend(parameter, options);
}
testf();
return this;
}
})(jQuery);
And now, this will work:
$('elm').sadstory({
call: function(e) {
e.something = 'no';
}
});
Here is my problem.
I am using Backbone js and every collection I have defined requires the same check on save or destroy. Except that the destroy success functions need to be passed an element to remove from the page when the destroy succeeds.
I didn't want to copy and paste the same code into every save or destroy method so I created this:
window.SAVE_TYPE_DESTROY = 'destroy';
window.SaveResponseHandler = function(el,type){
if (!type){
this.success = function() {
this._success();
};
}else if (type == window.SAVE_TYPE_DESTROY){
this.success = function() {
this._success();
$(el).remove();
};
}
};
SaveResponseHandler.prototype._success = function(model, response, options) {
if ((response.success * 1) === 0) {
persistError(model, {
responseText: response.message
}, {});
}
};
SaveResponseHandler.prototype.error = persistError;
var saveResponseHandler = new SaveResponseHandler();
And I use it like this:
destroy: function() {
var el = this.el;
var model = this.model;
this.model.destroy(new SaveResponseHandler(el,'destroy'));
},
change: function() {
this.model.set({
job_category_name: $($(this.el).find('input')[0]).val()
});
var viewView = this.viewView;
this.model.save(null, saveResponseHandler);
}
The problem is when success is called I get the following error:
Uncaught TypeError: Object [object Window] has no method '_success'
Any help will be much appreciated. I'm also open to any suggestions on better ways to handle this.
this inside of SaveResponseHandler.success isn't SaveResponseHandler, it's window.
window.SaveResponseHandler = function(el, type) {
var self = this;
if (!type) {
this.success = function() {
self._success();
};
} else if (type == window.SAVE_TYPE_DESTROY) {
this.success = function() {
self._success();
$(el).remove();
};
}
};
http://jsfiddle.net/ethagnawl/VmM5z/