Setting correct this-value in requestAnimationFrame - javascript

I have an app-object constructer that looks like this:
var app = function(loadedsettings) {
return {
init: function() {
this.loop();
},
loop: function() {
this.update();
window.requestAnimationFrame(this.loop);
},
update: function() {
//loop through settings and call update on every object.
},
settings: [
//array of settings objects, all with update methods.
]
};
}
Then when I do:
var theApp = app(settings);
theApp.init();
I get:
Uncaught TypeError: Object [object global] has no method 'update'
because when requestAnimationFrame is called, the this-value inside the loop function is set to window.
Does anybody know how to call requestAnimatinFrame with the 'theApp' object set as the this-value?

You can create a bound function (with a fixed this), and pass that to requestAnimationFrame:
var app = function(loadedsettings) {
return {
init: function() {
this.loop();
},
loop: function() {
this.update();
window.requestAnimationFrame(this.loop.bind(this));
},
update: function() {
//loop through settings and call update on every object.
},
settings: [
//array of settings objects, all with update methods.
]
};
}
I think that a browser which supports requestAnimationFrame will also support Function.prototype.bind, but in case you come across one that doesn't, there are polyfills available.

You need to cache a reference to this:
var app = function(loadedsettings) {
var self = this;
return {
init: function() {
self.loop();
},
loop: function() {
self.update();
window.requestAnimationFrame(self.loop);
},
** snip **
...

Related

Ext JS Hash Map not being destroyed after window closes

Basically just want to create a new hashmap once the window is closed because it currently will still have the old values after closing and rendering a new window.
data: {
classCheck: false,
hashTable: new Ext.util.HashMap(),
countValid: 0
}
listeners: {
afterrender: function() {
var scope = this;
window.addEventListener('resize', function() {
scope.updateWindow(scope);
});
this.on('close', function(scope) {
this.getViewModel().data.hashTable = new Ext.util.HashMap();
window.removeEventListener('resize', scope.updateWindow);
});
},
},
renderer: function(value, cell_field1, cell_field2, cell_field3) {
var hashmap = this.up('classroom').getViewModel().data.hashTable;
JavaScript uses prototyping to implement class inheritance. The windows are instances of a window class, and due to the nature of prototyping the window instances reference the properties defined on the class until an instance explicitly sets them.
Ext.define('Fiddle.Class', {
boolean: true,
object: {},
constructor: function() {
/* 1. Instance Prototype
{ {
boolean: true,
object: {}
} }
*/
this.boolean = false;
this.object.key = false;
this.object = {};
/* 2. Instance Prototype
{ {
boolean: false, boolean: true,
object: { key: true } object: { key: false },
instance: null
} }
*/
// This returns true.
return this.object.key;
}
});
Therefore, the window instances need to set the values of properties that are objects or arrays during initialization, so that each instance gets its own copy. This can be defined on the class, and it is usually done in the constructor so that the data is available as early as possible.
Ext.define('Fiddle.window.Window', {
extend: 'Ext.window.Window',
data: null,
constructor: function(config) {
this.data = {
classCheck: false,
hashTable: new Ext.util.HashMap(),
countValid: 0
};
this.callParent(arguments);
}
});

Calling a private/nested javascript function from an outer scope

I have my javascript code like this . Inside that I have an init() function and in that function I have an options JSON object and in that object I have a function defined as objectselected(). How I call that function in a button click event
I have tried like this WorkFlow.init().options.Objectselected() but it is not working,
var WorkFlow = {
connectionData: [],
selectedTouchpoints: [],
init: function () {
var options = {
palleteId: "myPaletteElement",
elementId: "playAreaContainer",
TextStoreList: ['One', 'Two', 'Three'],
LinkTextStoreList: $('#drpLinkType option').map(function () {
return this.text;
}).get(),
shapeList: ['RoundedRectangle', 'Circle', 'Rectangle', 'Ellipse', 'Square', 'Diamond', 'Card', 'Database'],
diagramUpdate: function (e) {
},
objectSelected: function (e) {
},
linkUpdate: function (e) {
},
initialize: function () {
}
myGraph = new Graph(options);
options.initialize();
},
}
How to call that function.
One way around is you can return options and than call it.
init: function () {
var options = {
...your code..}
return options;
},
and call it than
var options = WorkFlow.init();
options.Objectselected();
As it stands, you have no access to options because it's a local variable - that is, local to its scope.
To access its contents, you'll need to return it from init().
Think about it:
WorkFlow.init()
Currently this returns undefined, because your init() returns nothing. You're trying to chain like in jQuery, but that relies on the API always returning the instance. Your path finds a dead-end at init().
To fix this, have init() return options - or at least the part of it you want to access from outside - an "export".
So (basic example)
init: function() {
var options {
my_func: function() { }, //<-- we want outside access to this
private: 'blah' //<-- this can stay private - leave it out of the export
}
//return an export, exposing only what we need to
return {
my_func: options.my_func
}
}
You need to return options as it is inside init function's scope
var WorkFlow = {
connectionData: [],
selectedTouchpoints: [],
init: function () {
var options = {
palleteId: "myPaletteElement",
elementId: "playAreaContainer",
TextStoreList: ['One', 'Two', 'Three'],
LinkTextStoreList: $('#drpLinkType option').map(function () {
return this.text;
}).get(),
shapeList: ['RoundedRectangle', 'Circle', 'Rectangle', 'Ellipse', 'Square', 'Diamond', 'Card', 'Database'],
diagramUpdate: function (e) {
},
objectSelected: function (e) {
},
linkUpdate: function (e) {
},
initialize: function () {
}
myGraph = new Graph(options);
options.initialize();
return options;
},
}
And call it as WorkFlow.init().objectSelected();
Building on Patrick's comment, you'd need to return options from the init function:
var WorkFlow = {
connectionData: [],
selectedTouchpoints: [],
init: function () {
var options = {
palleteId: "myPaletteElement",
...
options.initialize();
return options;
},
}

Using getter and setter on an object property that has his own properties

I'm fairly new to getters and setters and am looking for a way to listen for changes in an object to store the data immediately, without calling a Save() function everytime a value gets changed. This is how I do it right now:
var myObject = {
Data: {
enabled: true,
show: false
},
Save: function () {
//store myObject.Data to local storage
},
Load: function () {
//load data from local storage and assign it to myObject.Data
},
doSomething: function () {
myObject.Load();
if (myObject.Data.enabled) {
myObject.Data.show = true;
myObject.Save();
}
}
Now I would like to optimize this code so everytime a property in myObject.Data is changed, myObject.Save() is executed. The problem I'm experiencing is that it seems only possible to define a getter for a property that has just one value, but not for a property that is an object itself.
var myObj = {
_Data: {
a: 0,
b: 1,
c: 3
},
set Data (a) {
console.log(a);
}
};
myObj.Data.a = 2;
This obviously doesn't work since myObj.Data is not an object and doesn't have the same properties as myObj._Data.
Thanks in advance for any help.
You are likely interested in the Proxy object.
I used a very simple debounce function callHandler in order to avoid calling the onSet method dozens of times during array modifications. Otherwise, [1, 2, 3].splice(0, 1) would call the set handler once per item in the original array.
'use strict';
var myObject = {
Data: {
a: [1, 2, 3],
b: {c: ['test']}
},
Save: function() {
console.log('Save called');
},
}
function recursiveProxy(target, onSet) {
// For performance reasons, onSet will only be called one millesecond
// after the set handler has last been called.
var timeout;
function callHandler() {
clearTimeout(timeout);
timeout = setTimeout(onSet, 1);
}
var recursiveHandler = {
get: function(target, property) {
// If the property is something that could contain another object,
// we want to proxy it's properties as well.
if (typeof target[property] == 'object' && target[property] != null) {
return new Proxy(target[property], recursiveHandler);
}
return target[property];
},
set: function(target, property, value) {
console.log('Set called - queueing onSet');
callHandler();
target[property] = value;
return true;
}
}
return new Proxy(target, recursiveHandler);
}
myObject.Data = recursiveProxy(myObject.Data, myObject.Save);
myObject.Data.a.splice(0, 1);
myObject.Data.b.c[0] = 'test 2';
I believe you are looking for Defining a getter on existing objects using defineProperty
To append a getter to an existing object later at any time, use
Object.defineProperty().
var o = { a:0 }
Object.defineProperty(o, "b", { get: function () { return this.a + 1; } });
console.log(o.b) // Runs the getter, which yields a + 1 (which is 1)
For e.g:
var Data = {
enable: true,
show: false
};
Object.defineProperty(Data, 'doSomething', {
get: function() {
// get something;
},
set: function(something) {
// set something
}
});

What is Vue way to access to data from methods?

I have the following code:
{
data: function () {
return {
questions: [],
sendButtonDisable: false,
}
},
methods: {
postQuestionsContent: function () {
var that = this;
that.sendButtonDisable = true;
},
},
},
I need to change sendButtonDisable to true when postQuestionsContent() is called. I found only one way to do this; with var that = this;.
Is there a better solution?
Inside methods if you don't have another scope defined inside, you can access your data like that:
this.sendButtonDisable = true;
but if you have a scope inside the function then in vue is a common usage of a variable called vm (stands for view model) at the beginning of the function, and then just use it everywhere like:
vm.sendButtonDisable = false;
An example of vm can be seen in the Vue official documentation as well.
complete example:
data: function () {
return {
questions: [],
sendButtonDisable : false
}
},
methods: {
postQuestionsContent : function() {
// This works here.
this.sendButtonDisable = true;
// The view model.
var vm = this;
setTimeout(function() {
// This does not work, you need the outside context view model.
//this.sendButtonDisable = true;
// This works, since wm refers to your view model.
vm.sendButtonDisable = true;
}, 1000);
}
}
It depends on how you call your postQuestionsContent method (if you call it asynchronously, you might need to bind the this context).
In most cases, you should be able to access it using this.$data.YOURPROPNAME, in your case this.$data.sendButtonDisable:
data: function () {
return {
questions: [],
sendButtonDisable : false
}
},
methods:
{
postQuestionsContent : function()
{
this.$data.sendButtonDisable = true;
}
}
Try this instead
...
methods:
{
postQuestionsContent ()
{
this.sendButtonDisable = true;
}
}
Registering your methods in the above manner should resolve the issue.
I tried both this.$data.sendButtonDisable and vm.sendButtonDisable and did not work for me.
But I got it working with outer_this = this, something like:
methods: {
sendForm(){
var outer_this;
outer_this = this;
$.ajax({
url: "email.php",
type: "POST",
dataType: "text",
data: {
abc: "..."
},
success: function(res){
if(res){
//...
outer_this.sendButtonDisable = false;
}else{
//...
}
}
});
}
},

Console logging "this" returns "null"

I am trying to create a flux store for a React app I am building. I am using an object-assign polyfill npm package and Facebook's Flux library.
Initially I was getting the error "Cannot read property '_data' of null' error in the console which was refering to var currIds = this._data.map(function(m){return m.id;});. That method is currently the only one being called directly. I then did console.log(this) which returned "null".
I find this strange. What is going on?
My code:
var Assign = require('object-assign');
var EventEmitterProto = require('events').EventEmitter.prototype;
var CHANGE_EVENT = 'CHANGE';
var StoreMethods = {
init: function() {},
set: function (arr) {
console.log(this);
var currIds = this._data.map(function(m){return m.id;});
arr.filter(function (item){
return currIds.indexOf(item.id) === -1;
}).forEach(this.add.bind(this));
},
add: function(item){
console.log(this);
this._data.push(item);
},
all: function() {
return this._data;
},
get: function(id){
return this._data.filter(function(item){
return item.cid === id;
})[0];
},
addChangeListener: function(fn) {
this.on(CHANGE_EVENT, fn);
},
removeChangeListener: function(fn) {
this.removeListener(CHANGE_EVENT, fn);
},
emitChange: function() {
this.emit(CHANGE_EVENT);
},
bind: function(actionType, actionFn) {
if(this.actions[actionType]){
this.actions[actionType].push(actionFn);
} else {
this.actions[actionType] = [actionFn];
}
}
};
exports.extend = function(methods) {
var store = {
_data: [],
actions: {}
};
Assign(store, EventEmitterProto, StoreMethods, methods);
store.init();
require('../dispatcher').register(function(action){
if(store.actions[action.actionType]){
store.actions[action.actionType].forEach(function(fn){
fn.call(null, action.data);
})
}
});
return store;
};
I can't see where set is called, however your this can be null if the function is invoked through call (see here) or apply, and your first argument is null.
This also happens in your require.register callback:
fn.call(null, action.data) //First parameter is your 'this'.

Categories