I have the following code:
someClass1 = function () {
this.doStuff = function () {
PubSub.publish('topic1', { id: 1 });
}
}
someClass2 = function () {
this.forename = 'bob2';
PubSub.subscribe("topic1", function (msg, data) {
log(msg, data, this.forename);
});
}
function log() {
console.log(arguments);
}
var c1 = new someClass1();
var c2 = new someClass2();
c1.doStuff();
and I am using the pubsubjs library (https://github.com/federico-lox/pubsub.js)
The code is simple - publish a message and handle it in another class (someClass2) using PubSub
My question is that when I publish a message and handle it in someClass2, this is undefined. This occurs at the line: log(msg, data, this.forename);
This means I cant access any of the someClass2 properties/functions. What do I need to do to get the this to not be undefined? Is this possible? Are they other libraries that will help? Am I doing it wrong....
All help apprenticed! Thanks
You're passing an unbound function to subscribe. Such a function has "no idea" about this. You have to bind it:
PubSub.subscribe("topic1", (function (msg, data) {
log(msg, data, this.forename);
}).bind(this));
this is not what you expect inside the callback, just cache a copy of this as another variable outside:
someClass2 = function () {
this.forename = 'bob2';
var that = this;
PubSub.subscribe("topic1", function (msg, data) {
log(msg, data, that.forename);
});
}
Related
I'm currently developing a WebSocket. For that purpose I've created a JavaScript class. In this class I've 2 methods: subscribe() and bind(). The first method subscribes to a channel and the next one should listen to it.
But when I call my methods this way:
let socket = new CustomWebSocket();
socket.subscribe("my-channel").bind("my-event", function (response) {
console.log(response);
});
I'm getting an error message in my console:
Uncaught TypeError: socket.subscribe(...).bind is not a function
This is how I wrote my functions inside my CustomWebSocket class:
subscribe(channelName) {
let self = this;
let subMsg = {
type: "subscribe",
channel: channelName
};
self.ws.send(JSON.stringify(subMsg));
return channelName;
}
bind(eventName, callback) {
let self = this;
self.ws.onmessage = function (event) {
let eventData = JSON.parse(event.data);
if (eventData.type === "channelMessage") {
callback(eventData.payload);
}
}
}
What did I wrong? It thought it can work this way...
You're returning channelName; from subscribe. So you're effectively trying to call bind on channelName.
To be able to call bind on the return value of subscribe, you need to return self or this from subscribe.
Is there possible to access a injected dependency on controller outside on it?
function clienteCreateController(ClientesService, recuperarEndereco) {
var vm = this;
vm.pesquisarCep = pesquisarCep;
}
function pesquisarCep(cep) {
recuperarEndereco.find(cep)
.success(function(data) {
parseEndereco(data).bind(this);
})
.error(function(err) {
// showAlertDanger(vm, 'Cep inválido.');
console.log(err);
});
}
I'm calling the method from a button click.
Thanks
I do not think you can access the parameter recuperarEndereco in the outside function pesquisarCep. Because inside the execution context in function pesquisarCep, the variable is recuperarEndereco is not declared at all. Same as this JSFiddle: http://jsfiddle.net/dLhmozf3/. It reports error:
function outsite () {
console.log('param: ' + param);
}
var f = function (param) {
var me = outsite;
me();
};
f();
You need to define the outside function like: function pesquisarCep(cep, recuperarEndereco) { .... And call it like pesquisarCep(cep, recuperarEndereco).
Resolved!! see the end of the question for the result that I used
I am trying to write a function that can handle my apps paging by routes.
I have a function route() that is called with argument being the route(page) to move to.
route is an object that defines a model that it uses that handles its logic.
This model contains 3 functions
indexAction
- This renders my view and appends it to my page.
bindEvents
- This is where I have placed all of my click events
shutDown
- This is instructions to run when moving to a new page
The router function first runs shutdown on the current page, here I have the $(selector).off() and $(selector).remove()
it then runs the enidexAction and bindEvents function.
My issue now is when I return to this page, all my click functions are running twice, then three times etc... its as if the off() never actually unbind from the anchor.
here is an example of one of my models
var NewPageModel = (function() {
var instance;
var modal = 'null';
function createInstance() {
var object = {
indexAction: indexAction,
shutDown: shutDown,
bindEvents: bindEvents
};
return object;
}
function indexAction (data, callback){
var partials = {};
ViewManager.render('pageName',{context:data}, partials,function(html){
ViewManager.appendUnique('#xxx',html,'uniqueID');
callback();
});
}
/**
* Remove modal
*/
function shutDown(){
this.modal.off();
this.modal.remove();
}
function bindEvents() {
if(this.modal!='null'){
return;
}
this.modal = $(PagerManager.pages.newGroup.id);
this.modal.on('click','div.close', function () {
shutDown();
});
this.modal.on('click', 'button.cancel', function () {
shutDown();
});
this.modal.on('click', 'button.submit', function () {
//code that submits form information
});
}
return {
getInstance: function () {
if (!this.instance) {
this.instance = createInstance();
}
return this.instance;
}
};
})();
EDIT!!
So I am still learning about the importance of scopes and how they can be applied to functions
Here is the working code
var NewPageModel = (function() {
var instance;
var modal;
function createInstance() {
var object = {
indexAction: indexAction,
shutDown: shutDown,
bindEvents: bindEvents
};
return object;
}
function indexAction (data, callback){
var partials = {};
ViewManager.render('pageName',{context:data}, partials,function(html){
ViewManager.appendUnique('#xxx',html,'uniqueID');
callback();
});
}
/**
* Remove modal
*/
function shutDown(){
this.modal.off();
this.modal.remove();
this.modal = null;
}
function bindEvents() {
//This is confused logic, if I use off() in shutdown, I don't need to do this as I need to bind all the events again. hence in shutdown modal=null;
if(!this.modal){
return;
}
this.modal = $('#modal');
this.modal.on('click','div.close', function () {
shutDown().apply(this);
}).bind(this);;
this.modal.on('click', 'button.cancel', function () {
shutDown().apply(this);
}).bind(this);;
this.modal.on('click', 'button.submit', function () {
//here I only use the apply(this) if I use another internal function
//code that submits form information
}).bind(this);;
}
return {
getInstance: function () {
if (!this.instance) {
this.instance = createInstance();
}
return this.instance;
}
};
})();
You are losing your this in the event handler functions (this will be the element clicked) so the shutDown is not getting the correct this:
this.modal.on('click','div.close', function () {
shutDown();
});
should be:
var self = this;
this.modal.on('click', 'button.cancel', function () {
self.shutDown();
});
e.g.
function bindEvents() {
var self = this;
if(this.modal!='null'){ /// <<<< !!!!!! WTF
return;
}
this.modal = $(PagerManager.pages.newGroup.id);
this.modal.on('click','div.close', function () {
self.shutDown();
});
this.modal.on('click', 'button.cancel', function () {
self.shutDown();
});
this.modal.on('click', 'button.submit', function () {
//code that submits form information
});
}
Note: I am ignoring the string comparison to null for now as I have no clue what you are doing there :)
As pointed out in comment by #Gurami Dagundaridze you can also retain the correct this using bind (I think the syntax goes like this):
this.modal.on('click', 'button.cancel', shutDown.bind(this));
In the spirit of keeping your syntax and just fixing the bug,
if(this.modal!='null'){ should be if(modal!='null'){
Because this.modal will be undefined at that condition and will just return.
In the spirit of fixing your code, you need to keep a reference to this or it will default to window in the browser.
var modal;
function createInstance() {
var object = {
modal : modal,
shutDown: shutDown,
bindEvents: bindEvents
};
return object;
}
function bindEvents() {
if(this.modal){
return;
}
// ..... //
this.modal.on('click','div.close', function () {
shutDown.apply(this);
}.bind(this));
// ..... //
}
Working demo :
http://jsfiddle.net/uyovgdj3/
I've just started using AngularJS and I love it.
However - I have a need to save an item to my database using $resource and then get back and object containing the values of the newly created item in the database (especially the database-assigned ID).
I've found a few articles describing this - but none of them seems to work for me :(
I have a very simple setup:
var app = angular.module("todoApp", ['ngResource', 'ngAnimate']);
app.factory("TodoFactory", function ($resource) {
return $resource('.../api/todo/:id', { id: '#id' }, { update: { method: 'PUT' }});
});
var todoController = app.controller("TodoController", function ($scope, TodoFactory) {
$scope.todos = [];
init();
function init() {
$scope.todos = TodoFactory.query();
}
$scope.addTodo = function () {
TodoFactory.save($scope.item, function () {
// Success
console.log($scope.item); // <--- HERE'S MY PROBLEM
$scope.todos.push($scope.item);
$scope.item = {};
},
function () {
// Error
});
};
But when I call TodoFactory.save, the $scope.item does not contain the Id-property from the database - only the values it had upon calling save.
How can I get my setup to return the updated object with all the database-generated values?
If somebody could point me in the right direction it would be much appreciated :)
Update: I just went over the source for the API I've been supplied - the save-method doesn't update the object that's inserted.
After I fixed this "minor" issue, peaceman's example worked like a charm.
Sorry for the inconvenience everybody - but thank you very much for the responses! :)
The save method of the TodoFactory won't update the $scope.item, but instead calls the callback function with the saved object as a parameter, that contains the new id.
So you have to replace
$scope.addTodo = function () {
TodoFactory.save($scope.item, function () {
// Success
console.log($scope.item); // <--- HERE'S MY PROBLEM
$scope.todos.push($scope.item);
$scope.item = {};
},
function () {
// Error
});
};
with
$scope.addTodo = function () {
TodoFactory.save($scope.item, function (savedTodo) {
// Success
console.log(savedTodo);
$scope.todos.push(savedTodo);
$scope.item = {};
},
function () {
// Error
});
};
This behaviour is documented at ngResource.$resource
This should solve the problem.
$scope.addTodo = function () {
// ADD IN A VARIABLE FOR THE RESPONSE DATA AS THE PARAMETER TO YOUR SUCCESS CALLBACK FN
TodoFactory.save($scope.item, function (responseItem) {
// Success
console.dir(responseItem); // <--- AND THEN USE IT
$scope.todos.push(responseItem);
$scope.item = {};
},
function () {
// Error
});
};
Yes, I have thoroughly searched google and did not find anything that suits my requirement.
The code i have so far is at the link below:
http://jsfiddle.net/ZKwTY/4/
There are multiple onchange events which call almost the same code, i would like to combine them maybe in a comma separated fashion to call it only once.
something like this
(on1Change, on2Change, on3Change): function () {
this.loadData();
}
is this possible??
Note: these functions are bound to the controls via a framework over which i do not have control, i need to create these functions and the framework would bind these to the respective controls
or you can create your object like this
var ol = {
on1Change: this.loadData,
on2Change: this.loadData,
on3Change: this.loadData,
on4Change: this.loadData,
loadData: function () {
this.loadData1();
this.loadData2();
},
loadData1: function () {
alert('hi from loadData1');
},
loadData2: function () {
alert('hi from loadData2');
}
};
Then if you want to do it once, then declare a object
var ol = {
loadData: function () {
this.loadData1();
this.loadData2();
},
loadData1: function () {
alert('hi from loadData1');
},
loadData2: function () {
alert('hi from loadData2');
}
};// end of object
ol.on1Change = ol.on2Change = ol.on3Change = ol.on4Change = ol.loadData;
add all propteries dynamically after object declaration
use bind()
$("selector").bind(on1Change, on2Change, on3Change): function () {
this.loadData();
}.....
you can try somethig like this http://jsfiddle.net/s4VVY/
i.e. add methods after object create
[1,2,3,4,5].forEach(function(it){ol["on"+it+"Change"] = function(){this.loadData()}})
UPDATE
may be this help
var ol = (function(){
var o = {
loadData: function () {
this.loadData1();
this.loadData2();
},
loadData1: function () {
alert('hi from loadData1');
},
loadData2: function () {
alert('hi from loadData2');
}
}
o.on1Change=o.on2Change=o.on3Change=o.on4Change=function(){ this.loadData();};
return o;
})()
also you can make function bindFunc
function bindFunc(){
var obj = arguments[0],
handler = arguments[1],
properties = Array.prototype.slice.call(arguments,2);
for(var i in properties){
obj[properties[i]] = handler;
}
}
and call as
bindFunc(o,function(){this.loadData();},"on1Change","on2Change","on3Change","on4Change")