Trouble while accessing to a method - javascript

I'm beginning to use jquery and Model View ViewModel and I encounter a problem with the utilisation of this with the Event Handler Attachment : on().
My first class is TicTacToeModel which manipulates a TicTacToe game in memory :
var TicTacToeModel = function () {
Observable.call(this);
this.grid = new Array(3);
for (let i = 0; i < this.grid.length; ++i) {
this.grid[i] = new Array(3);
}
};
//Some prototype function
//...
I have another class, TicTacToeController which depends on the first class and which manages the graphic part of the game with a manipulation of the DOM :
var TicTacToeController = function(game, selector) {
this._element = $(selector);
this._model = game;
this._template = Handlebars.compile(view);
this.addListeners();
this.render();
};
(declaration of game : game = new TicTacToeModel();)
So in my second class I have this function :
TicTacToeController.prototype.addListeners = function() {
this._model.on('change', this.render, this);
this._model.play(0,0);//works
this._element.on('click', "td", function() {
this._model.play(0,0);//doesn't work
});
};
And I would like to call the play() function in the cell (0,0) (the function play updates the game in memory) when I click on the cell in my graphic interface but I cannot do it in the .on(). But that seems to be working outside of the .on() function so I suppose a bad utilisation of this causing the problem.

You need to use bind like this.
Change this:
this._element.on('click', "td", function() {
this._model.play(0,0);//doesn't work
});
to:
this._element.on('click', "td", function() {
this._model.play(0,0); //should now work
}.bind(this));

You are not in the same scope, it's not the same this variable you are employing when calling the play method.
A dirty workaround could be
let _model = this._model
this._element.on('click', "td", function() {
_model.play(0,0);//work!
});
But as said it's a dirty workaround, maybe someone else could explain but basically think this produces a memory leak. Maybe the solution would be to use a method in the same class and pass the instance to the click method, kind of:
TicTacToeController.prototype.click = function() ...
...
this._element.on('click', "td", this.click);
Think this should do the trick, but I must admit I'm not a js expert.

Related

Knockout subscribe/event type system without observable?

I want to make use of the subscribe() function of knockout js to manually trigger an event at a certain point.
I could make an observable() and everytime put a GUID in there to trigger the scubscribe.
Is there a cleaner way within Knockout js to have a typical event-like structure?
Edit
Ok, apparently I can use observable.valueHasMutated() - might already a a bit cleaner that using a GUID.
Example
This is the behaviour that I'm looking for:
function Car()
{
var self = this;
self.onOpenDoor = ko.observable();
self.openDoor = function()
{
// using an observable / valueHasMutated for this feels a bit hacky
// is there an other way to use the underlying subscribe() system?
self.onOpenDoor.valueHasMutated();
}
}
var car = new Car();
// multiple subscribers
car.onOpenDoor.subscribe(function()
{
console.log('Do something');
})
car.o**nOpenDoor.subscribe(function()
{
console.log('Do something else');
})
car.openDoor();
I am aware this is not the default 'knockout' way to do stuff - that is not what this question is about.
Update
After #RoyJ's reference to Niemeyer's blog I went for this solution:
function Car()
{
var self = this;
self.onOpenDoor = new ko.subscribable();
self.openDoor = function()
{
self.onOpenDoor.notifySubscribers();
}
}
Update If you're just looking for clarity, you can use notifySubscribers instead of valueHasMutated. You might want to take a look at the base type of observables, ko.subscribable.
I would do it like this:
var vm = {
///...definitions...
openCount: ko.observable(0),
openDoor: function () {
vm.openCount(vm.openCount()+1);
}
};
vm.openCount.subscribe(function () {
///...do something
});
vm.openCount.subscribe(function () {
///...do something else
});
ko.applyBindings(vm);
Demo http://jsfiddle.net/uoqdfhdb/2/

How to call a function of a parent class from another function in jquery

I guess that's the simple question. I'm new in js, especially in Backbone.js.
All I want to know is how I can refer to my function inside jquery function.
getLanguages: function() {
...
return languages;
},
render: function() {
...
$("input[type='checkbox']").bind("change", function() {
// todo: getLanguages
});
}
I tried to get languages via this but, of course, I got checkbox in this case.
Edit:
It's so simple. Many thanks to all!!!
This is a classic problem in Javascript. You need to reference this inside a callback, but this changes to the element being bound to. A cheap way to do it:
render: function() {
var that = this;
$("input[type='checkbox']").bind("change", function() {
that.getLanguages();
});
}
that will stay defined as the this that render is defined on.
However, you’re using Backbone, and it has more suitable ways to handle this situation. I don’t know the name of your Backbone.View class, but here’s an example adapted from the documentation:
var DocumentView = Backbone.View.extend({
events: {
"change input[type='checkbox']": "doSomething"
},
doSomething: function() {
this.getLanguages(); # uses the correct this
}
});
Calling bind inside render is not The Backbone Way. Backbone views are made to handle event delegation without the unfortunate need to pass this around.
Save this object before bind change event in the scope of render function.
render: function() {
var CurrentObj = this;
$("input[type='checkbox']").bind("change", function() {
CurrentObj.getLanguages();
});
}
You can save the appropriate object into a local variable so from the event handler, you can still get to it:
getLanguages: function() {
...
return languages;
},
render: function() {
...
var self = this;
$("input[type='checkbox']").bind("change", function() {
var lang = self.getLanguages();
...
});
}

Is it a bad practice to add reference to a Javascript object in one of its attributes?

Let's say I have a setup like this.
var Account = function(data) {
this.data = data;
this.domElement = (function(){ code that generates DOM element that will represent this account })();
this.domElement.objectReference = this;
}
Account.prototype = {
show: function() { this.domElement.classList.remove('hidden'); },
hide: function() { this.domElement.classList.add('hidden') }
}
My question is about the last line: this.domElement.objectReference = this;
It would be a useful thing to have because then I can add event listeners to my DOM element and still get access to the object itself. I can add methods that would affect my DOM element, such as hide() and show(), for instance, without having to resort to modifying visibility of the DOM element using CSS directly.
I tested this code and it works like I want it to, but I'm curious whether this would cause memory leaks or some other unpleasantness or if it's an acceptable thing to do?
Thank you!
Luka
I know this has been answered by #PaulS. already, but I find the answer counter intuitive (returning a DOM element from the Account constructor is not expected) and too DOM-centric, but at the same time the implementation is very simple, so I am not sure what to think ;)
Anyway, I just wanted to show a different way of doing it. You can store Account instances in a map and give them a unique id (perhaps they have one already), then you store that id as a data attribute on the DOM element. Finally you implement a getById function or something similar to retrieve the account instance by id from the listeners.
That's pretty much how jQuery's data works.
Here's an example with delegated events like you wanted from the comments.
DEMO
var Account = (function (accounts, id) {
function Account(data) {
accounts[this._id = ++id] = this;
this.el = createEl.call(this);
}
Account.prototype = {
constructor: Account,
show: function() { this.el.classList.remove('hidden'); },
hide: function() { this.el.classList.add('hidden'); }
};
function createEl() {
var el = this.el = document.createElement('div');
el.className = 'account';
el.innerHTML = el.dataset.accountId = this._id;
return el;
}
Account.getById = function (id) {
return accounts[id];
};
Account.init = function () {
//add delegate listeners
document.addEventListener('click', function (e) {
var target = e.target,
account = Account.getById(target.dataset.accountId);
if (!account) return;
account.hide();
});
};
return Account;
})({}, 0);
//once DOM loaded
Account.init(); //start listening to events
var body = document.body;
body.appendChild(new Account().el);
body.appendChild(new Account().el);
Why not have domElement as a variable, and return it from your function? To keep the reference to your constructed Object (but only where this is as expected), you could do a if (this instanceof Account) domElement.objectReference = this;
You've now saved yourself from circular references and can access both the Node and the Object. Doing it this way around is more helpful if you're expecting to lose the direct reference to your Account instance, but expect to need it when "looking up" the Node it relates to at some later time.
Code as requested
var Account = function (data) {
var domElement; // var it
this.data = data;
domElement = (function(){/* ... */}()); // use var
if (this instanceof Account)
domElement.objectReference = this; // assign `this`
return domElement;
};
// prototype as before
Returned element is now the Node, not the Object; so you'd access the Account instance like this
var domElement = new Account();
domElement.objectReference.show(); // for example
In my opinion there is nothing good about referencing the object inside of the object itself. The main reason for this is complexity and obscurity.
If you would point out how exactly are you using this domElement.objectReference later in the code, I am sure that I or someone else would be able to provide a solution without this reference.

JavaScript: How to bind a method?

JSFiddle: http://jsfiddle.net/M2ALY/3/
My goal is to make a module that I can use and distribute. Therefore I must not pollute the global namespace. The module I'm making is also going to be used multiple times on one web page. That's why I chose to use OOP, but this introduced a problem.
I want my object to bind a function to be run when the user clicks an element in the DOM. In this simplified example I made, I want an alert box to pop up when the user clicks a paragraph. As an example, one of the things I need in the real project I'm working on is: The user clicks a canvas, the function figures out where the user clicked and saves it to this.clientX and this.clientY.
Instead of doing
this.bind = function() {
$("p1").bind('click', function() {
// code here
});
}
I figured it would work if I did:
this.bind = function() {obj.codeMovedToThisMethod()}
The problem is that this isn't a good design. Inside the "class" you shouldn't need to know the name of the object(s) that is going to be made of this "class". This doesn't get better when I'm making multiple objects of the "class"...
So I figured I could do
$("p1").bind('click', function(this) {
// code here
});
}
But it didn't work because sending this into the function didn't work as I thought.
How should I solve this problem?
Here is a simplified sample problem. (Same as JSFiddle.)
var test = function() {
this.alert = function() {
alert("Hi");
}
this.bind = function() {
$("#p1").bind('click', function() {
obj.alert();
});
}
}
window.obj = new test();
obj.bind();
// What if I want to do this:
var test2 = function() {
// Private vars
this.variable = "This secret is hidden.";
this.alert = function() {
alert(this.variable);
}
this.bind = function() {
$("#p2").bind('click', function(this) {
obj2.alert();
this.alert();
});
}
}
window.obj2 = new test2();
obj2.bind();​
Thanks!
Read MDN's introduction to the this keyword. As it's a keyword, you can't use it as a parameter name.
Use either
this.bind = function() {
var that = this;
$("#p2").on('click', function(e) {
that.alert();
// "this" is the DOM element (event target)
});
}
or $.proxy, the jQuery cross-browser equivalent to the bind() function:
this.bind = function() {
$("#p2").on('click', $.proxy(function(e) {
this.alert();
}, this));
}

Automatic _.bindAll() in backbone.js

Is there a way to automatically do an _.bindAll for a backbone.js object?
I was talking to someone a while ago and they said that there was, but I have no idea where to start looking.
Example:
var TheView = Backbone.View.extend({
initialize: function() {
// HOW CAN I AVOID HAVING TO DO THIS?---->
_.bindAll(this,'render','on_element_01_click', 'on_element_02_click');
},
events: {
'click #element_01': 'on_element_01_click',
'click #element_02': 'on_element_02_click',
},
render: function(){
return this;
},
on_element_01_click: function(){
},
on_element_02_click: function(){
}
}
Do this instead:
_.bindAll(this);
Will bind ALL functions in this view.
I've since learned of a easier technique if you want to build bindAll in to your views (which is handy for things like AJAX callback methods that aren't auto-bound the way event handlers are). Basically you just override the constructor to perform the auto-binding.
var BoundModel = Backbone.Model.extend({
constructor: function() {
Backbone.Model.apply(this, arguments);
if (this.boundMethods) {
_(this).bindAll.apply(this, this.boundMethods);
}
}
})
var SubclassOfBoundModel = Backbone.Model.extend({
boundMethods: ['handleFetchResponse'],
initialize: function () {
this.model.on('sync', this.handleFetchResponse);
}
handleFetchResponse: function() {
// this function is bound to the model instance
}
})
Of course if you just wanted to bind all your methods you could leave out the "boundMethods" part and just have:
constructor: function() {
Backbone.Model.apply(this, arguments);
_(this).bindAll();
}
I tried doing this myself and I was able to get it working with something like this:
function bindOnExtend(clazz) {
var originalExtend = clazz.extend;
clazz.extend = function() {
var newSubClass = originalExtend.apply(this, arguments);
var originalInitialize = newSubClass.prototype.initialize;
newSubClass.prototype.initialize = function() {
// The constructor will get broken by bindAll; preserve it so _super keeps working
var realConstructor = this.constructor;
_.bindAll(this);
this.constructor = realConstructor;
originalInitialize.apply(this, arguments);
};
return bindOnExtend(newSubClass);
};
return clazz;
}
var BoundModel = Backbone.Model.extend();
bindOnExtend(BoundModel);
var BoundView = Backbone.View.extend();
bindOnExtend(BoundView);
However, I wouldn't recommend it. Doing that will make closures for every single method on every single model/view/whatever you instantiate. Not only does that add a slight increase in overall memory usage, it also opens up the possibility of memory leaks if you're not careful. Furthermore, it makes your stacktraces several lines longer, as they have to wind through bindOnExtend.
In my experience, having to do "_.bindAll(this, ..." is worth the trouble because:
1) it makes my code more clear/obvious to anyone coming after me
2) it encourages me to qualify my bindAll, instead of just using the 1-arg form
3) I hate wading through long stacktraces
But, if you want it the above code should work.

Categories