cannot modify nodejs module value - javascript

I want initialize a module with some default values and change them later if required. To do this I have a module/singleton which contains a _state value. I have exposed a setter method to update that value. However, the setter does not update the _state member variable. The code looks like this:
var StateObject = function () {
var _state = { a: 1 };
return {
state : _state,
setState : function (s) {
_state = s;
}
};
}();
modules.export = StateObject;
and the calling function:
var SO = require('./state-object');
console.log(SO.state.a); // prints 1
SO.setState({a: 2});
console.log(SO.state.a); // still prints 1
Can anyone explain what would cause this and if there is a work around?

The potential pitfall of a solution like this is if some piece of code stores SO.state locally and references that. If that happens and you call setState() some time later, the reference won't be updated in that piece of code. That's just something to be aware of when you replace the whole state and not just individual values in the state.
The problem here has to do with references. When you execute StateObject(), the state variable stores the initial reference to _state. Then when you call setState(), you overwrite _state, but state is still holding on to the previous reference.
You might try something like this:
modules.export = {
state: { a: 1 },
setState: function(v) {
this.state = v;
}
};

Well, the problem is with the reference not being updated, as mscdex mentioned.
In my opinion the main problem is actually your logic: why have a setter if you don't have a getter?
var SO = function () {
var _state = { a: 1 };
return {
getState : function () {
return _state;
},
setState : function (s) {
_state = s;
}
};
}();
console.log(SO.getState().a); // prints 1
SO.setState({a: 2});
console.log(SO.getState().a); // prints 2
This works, as it is also returning the reference to the latest set object. And this has actually nothing to do with node or modules, you can run your example in the browser's JavaScript console.

Related

How to fix scope problem inside "then" function react?

I am trying to print out of Printer. I am a little new to react and javascript. I am trying to pass the state to a then function of Third Party Code. But i am getting an error:
Cannot read property 'restaurant_name' of undefined
How can i pass state to the scope of then function of qz?
print = () => {
let { state } = this.state;
qz.websocket.connect()
.then(function() {
return qz.printers.find("BillPrinter");
}).then(function(printer) {
var config = qz.configs.create(printer);
var data = [
`${state.restaurant_name}` + '\x0A',
`${state.restaurant_address}`
]
return qz.print(config, data);
});
}
You have some unnecessary destructuring that is causing your error - this.state.state doesn't exist, yet this line:
let { state } = this.state;
Is equivalent to:
let state = this.state.state;
Remove the curly braces and it'll work fine.
let state = this.state;
Also note that state will be a reference to this.state rather than being another object.
Use arrow function to keep the function in the upper scope as #Ali Torki suggested:
.then(printer => {....})

Variable assigned to the property of a global object not updating

I'm running into an issue where a variable assigned to the property of a global object does not get updated. I know this is some kind of javascript pass by reference issue that i'm not understanding, despite having looked through some other similar SO questions. Here's the some code I wrote to test this scenario:
function formState (type) {
this.current_form = { primary:null};
this.set_primary = function(form) {
this.current_form.primary = form;
return this.current_form.primary;
};
}
var schedule = function(someState) {
someState.set_primary({"updated":"updated"});
};
var state = new formState();
var newState = state.set_primary({"new":"new"});
console.log("newState = ", newState);
schedule(state);
console.log("newState = ", newState);
A console.log at the end shows that newState = { new: 'new' }. Why is this? If newStateis referencing a block of memory, shouldn't I be able to update that memory from anywhere and have the changes be reflected in newState? I'm missing something fundamental here, any help would be appreciated.
Here's the corresponding repl.
You set newState equal to {new:'new'} with this line:
var newState = state.set_primary({"new":"new"});
Note that newState does not hold any reference to state or to state.current_form
So when you run this line:
schedule(state);
It changes state.current_form.primary, but it has no effect on the { new: 'new' } object that was assigned to newState.
If you WANT to see a change you can do this:
var state = new formState();
var newState = state.current_form;
state.set_primary({"new":"new"});
console.log("newState = ", newState.primary);
schedule(state);
console.log("newState = ", newState.primary);
this will print:
newState = { new: 'new' }
newState = { updated: 'updated' }
Note the difference. In this version of the code, newState is set to the current_form object which is not replaced by the call to set_primary. Now, when state.current_form.primary is set to reference the object {updated:'updated'}, newState.primary points to the updated object.
At first, this.current_form.primary refers the object {"new":"new"} and that is returned to be assigned in newState. At this point both newState and this.current_form.primary refer the same object. You can confirm that like this
console.log(newState === state.current_form.primary);
But, the next time, when you call schedule, it assigns a new object to current_form.primary. So, current_form.primary and newState point to different objects now. That is why newState still shows {new: "new"}.

How can I store functions within the HTML5 history states

So I'm using the HTML5 history management for adding the ability to navigate back and forward within a website with AJAX loaded subcontent.
Now I would like to store javascript functions within the state object, to callback at the state popping. More or less like the following code:
$(window).bind("popstate", function(event) {
var state = event.originalEvent.state;
if (!state) {
return;
}
state.callback(state.argument);
}
function beforeLoad() {
var resourceId = "xyz";
var func;
if (case1) {
func = switchPageToMode1;
} else { // case 2
func = swithPageToMode2;
}
func(resourceId); // run the function
window.history.pushState({ callback: func, resourceId: resourceId }, "newTitle", "newURL"); // and push it to history
}
function switchPageToMode1(resourceId) {
alterPageLayoutSomeWay();
loadResource(resourceId);
}
function swithPageToMode2(resourceId) {
alterPageLayoutSomeOtherWay();
loadResource(resourceId);
}
function loadResource(resourceId) {
...
}
All right. So what I'm trying to do is storing a reference to a javascript function. But when pushing the state (the actual window.history.pushState call) the browser files a complaint, namely Error: "DATA_CLONE_ERR: DOM Exception 25"
Anybody knows what I'm doing wrong? Is it at all possible to store function calls within the state?
No, it's not possible, not directly anyway. According to MDC the "state object," i.e. the first argument to pushState, "can be anything that can be serialized." Unfortunately, you can't serialize a function. The WHATWG spec says basically the same thing but in many more words, the gist of which is that functions are explicitly disallowed in the state object.
The solution would be to store either a string you can eval or the name of the function in the state object, e.g.:
$(window).bind("popstate", function(event) {
var state = event.originalEvent.state;
if ( !state ) { return; }
window[ state.callback ]( state.argument ); // <-- look here
}
function beforeLoad() {
var resourceId = "xyz",
func
;
if ( case1 ) {
func = "switchPageToMode1"; // <-- string, not function
} else {
// case 2
func = "swithPageToMode2";
}
window[ func ]( resourceId ); // <-- same here
window.history.pushState(
{ callback : func,
argument : resourceId
},
"newTitle", "newURL"
);
}
Of course that's assuming switchPageToMode1 and -2 are in the global context (i.e. window), which isn't the best practice. If not they'll have to be accessible somehow from the global context, e.g. [window.]MyAppGlobal.switchPageToMode1, in which case you would call MyAppGlobal[ func ]( argument ).
I came up with a slightly different solution.
I added two variables to the window variable
window.history.uniqueStateId = 0;
window.history.data = {}.
Each time I perform a pushstate, all I do is push a unique id for the first parameter
var data = { /* non-serializable data */ };
window.history.pushState({stateId : uniqueStateId}, '', url);
window.history.data[uniqueStateId] = data;
On the popstate event, I then just grab the id from the state object and look it up from the data object.
Here is what I do:
Each HTML page contains one or more components that can create new History entries.
Each component implements three methods:
getId() which returns its unique DOM id.
getState() that returns the component's state:
{
id: getId(),
state: componentSpecificState
}
setState(state) that updates the component's state using the aforementioned value.
On page load, I initialize a mapping from component id to the component like so:
this.idToComponent[this.loginForm.getId()] = this.loginForm;
this.idToComponent[this.signupForm.getId()] = this.signupForm;
Components save their state before creating new History entries:
history.replaceState(this.getState(), title, href);
When the popstate event is fired I invoke:
var component = this.idToComponent[history.state.id];
component.setState(history.state);
To summarize: instead of serializing a function() we serialize the component id and fire its setState() function. This approach survives page loads.

Need some help understanding basics about JS module pattern

I am trying to organize my code using the revealing module pattern.
I have a very basic question about how to set up a setter method.
$(document).ready(function() {
var designs = (function() {
var curRow,
setCurRow = function(val) {
curRow = val;
},
initTable = function() {
setCurRow(0);
};
return {
curRow : curRow,
setCurRow : setCurRow,
initTable : initTable
}
}) ();
designs.initTable();
designs.setCurRow(someNewVal);
console.log(designs.curRow);
});
The problem is that i dont get the someNewVal in the console output, I get undefined instead! I have a feeling I am doing something pretty silly here.
You can also solve this in another way by understanding the scopes of the variables and functions involved.
When you return your object constructor { curRow: curRow ... }, that just initializes the object member named curRow to the value of the variable curRow in the scope of the anonymous function; it doesn't create any persistent connection between them.
Once the anonymous function returns, calling designs.setCurRow is updating the curRow variable in that scope exactly as you expect, but that variable is now totally inaccessible to the outside world -- there is no connection between it and the curRow member of designs.
You can solve this by making the setCurRow method operate on this.curRow, as in the other solutions. In that case you don't need to make curRow a variable in the original scope, since it's entirely unused. The other solution is to add a 'getter' method to your current one:
var designs = (function() {
var curRow,
setCurRow = function(val) {
curRow = val;
},
getCurRow = function() {
return curRow;
},
initTable = function() {
setCurRow(0);
};
return {
getCurRow : getCurRow,
setCurRow : setCurRow,
initTable : initTable
};
}) ();
designs.initTable();
designs.setCurRow(someNewVal);
console.log(designs.getCurRow());
Because getCurRow and setCurRow are functions that are closed in the scope containing the variable varRow, they can reach back into that scope and access and change variables that are only accessible within it.
In this case making curRow a member of the object you return is probably simpler, but the other way is useful too since you can use it to create effectively private members and methods.
Looks like you want an object, not a module:
$(document).ready(function() {
var designs = {
setCurRow: function(val) {
this.curRow = val;
},
initTable: function() {
this.setCurRow(0);
},
curRow: 0
};
designs.initTable();
designs.setCurRow(someNewVal);
console.log(designs.curRow);
});
The problem is that setCurRow sets the value of the variable curRow after designs.curRow has already been set. Consider something like this:
var a = 1;
b = a; // sets b = a = 1
b = 2; // sets b = 2; leaves a = 1
Your code is doing the same thing, but with object-properties and setter methods to make it look complicated. :-)
As ruakh pointed out, you never re-assign curRow on the returned object, so it is always the default value. Change it to:
setCurRow = function(val) {
this.curRow = curRow = val;
},
And everything should work*.
* At least mostly - you won't be able to use call and apply on setCurRow (or pass it to setTimeout or setInterval without binding it first to your object (designs), since this is bound at call time in JavaScript.

Change var in object literal function

Hi guys I am writing some code using the object literal pattern, I have function that returns a value:
'currentLocation': function() {
var cL = 0;
return cL;
},
I then need to update the variable 'cL' from another function like this:
teamStatus.currentLocation() = teamStatus.currentLocation() + teamStatus.scrollDistance();
This part is part of another function - however I get an error back stating: invalid assignment left-hand side
I am guessing I can not update the variable in this way, could anyone suggest a better method or point me in the right direction.
Any help would be greatly appreciated.
Going to add more code to highlight what I am trying to do:
'currentLocation': function() {
var cL = 0;
return cL;
},
'increaseTable': function() {
if (teamStatus.currentLocation() <= teamStatus.teamStatusTableHeight() ) {
teamStatus.currentLocation = teamStatus.currentLocation() + teamStatus.scrollDistance();
$("#tableTrackActual").animate({scrollTop: (teamStatus.currentLocation)});
$("#tableMembers").animate({scrollTop: (teamStatus.currentLocation) });
//console.log(teamStatus.currentLocation());
teamStatus.buttonRevealer();
}
}
As you can see increaseTable should update the value of currentLocation - help this sheds more light on what I am trying to achieve.
You're writing teamStatus.currentLocation() =, which calls the function teamStatus.currentLocation and tries to assign to the return value. That isn't valid. You want just teamStatus.currentLocation = — no function call.
The variable inside your function is completely private to that function (and any functions defined within it). If you need to create a number of functions that share a set of private variables, you can do that with a closure. For instance:
var Thing = (function() {
var thingWideData;
function getData() {
return thingWideData;
}
function setData(newData) {
thingWideData = newData;
}
return {
getData: getData,
setData: setData
};
})();
What that does is create a Thing object which has getData and setData functions available for it, which get and set the completely private thingWideData variable contained by the anonymous closure. More about this pattern here and here, although the latter of those is more about private methods than private data.
What your code produces is:
0 = 0 + <some number>
Which variable do you want to update? cL? You are declaring it in the function, you cannot assign a value to it from outside. Depending on the rest of your code, you might be better off with getters and setters:
var object = {
_cL = 0,
get currentLocation() {
return this._cL;
},
set currentLocation(value) {
this._cL = value;
}
}
then you can do:
teamStatus.currentLocation = teamStatus.currentLocation + teamStatus.scrollDistance();
Update:
Regarding IE: If currentLocation should actually be just a number, it might be sufficient to just declare it as property:
var obj = {
currentLocation: 0
}

Categories