ReactJS variable update without the code to be executed - javascript

I'm getting out of my mind...
If someone can explain this to me:
componentDidUpdate(prevProps, prevState) {
let categoriesChange = false
console.log('check', JSON.stringify(this.checkedd), JSON.stringify(this.props.checked))
if (JSON.stringify(this.checkedd) !== JSON.stringify(this.props.checked)) {
console.log('did')
categoriesChange = true
this.checkedd = this.props.checked
console.log('didit', JSON.stringify(this.checkedd), JSON.stringify(this.props.checked))
}
let currentQuery = this.returnQueryString(prevProps.filter);
let nextQuery = this.returnQueryString(this.props.filter);
if (categoriesChange) {
if (nextQuery.send) {
this.props.refresh(nextQuery.queryString);
}
}
}
In this piece of code, the variable this.checkedd, which is instantiated with null in constructor, updates without entering in the JSON.stringify if.
Whenever Checked props(this.props.checked) is updated, the first console log shows them equal without entering in the if to show the "did" console.log.
Can someone explain how is this even possible? that a variable updates without the piece of code to be executed?

That's because objects are reference types in javascript. When you say, this.checkedd = this.props.checked, you are assigning the reference of the this.props.checked object to this.checkedd and not the actual value.
So when this.props.checked gets updated, this.checked will also reflect that change since they are both referring to the same object in memory.
Also, the way you are trying to achieve what you want is quite weird. You don't need to store the previous props value in an instance variable, i.e. this.checkedd. The componentDidUpdate hook has two parameters to it, prevProps and prevState. You could just check if the categories have changed by comparing the values in prevProps.checked and this.props.checked.

Related

Reading a sessionStorage property and creating an if statement

So, I want to read a value from sessionStorage. I know how to do this, but then I want to do something like this:
function whiteSet() {
if (// condition: what the heck would I put here?) {
const color = localStorage.getItem('theme');
//if the value 'theme' is equal to 'aluminum' then:
document.getElementById("headerbar").style.backgroundImage = "url(images/themes/aluminum.png)";
} else {
document.getElementById("headerbar").style.backgroundImage = "url(images/themes/galaxy.png)";
};
I don't really know what to do :/
If an answer already exists for this question, I have already tried finding it and failed.
Just take it step by step. First you're reading the value out of local storage and putting it in the variable 'color', then you need to decide what to do based on what value you read out. So instead of starting out with an if statement, get color first and then the conditional tests against its content:
function whiteSet() {
const theme = localStorage.getItem('theme');
if (theme === "aluminum") {
document.getElementById("headerbar").style.backgroundImage = "url(images/themes/aluminum.png)";
} else {
document.getElementById("headerbar").style.backgroundImage = "url(images/themes/galaxy.png)";
};
}
(Personally, since you're storing it in a localStorage key named "theme", I'd name the variable "theme" too instead of "color" just to avoid confusion; but that's more of a coding style thing than a requirement for it to work.)

How to determine if an array of string objects is empty using ngIf?

I have to write a code in which I must transfer some items between two lists and hide an error when the array of object literals to be transferred to isn't empty. I have created two controllers(to manage two separate lists) and one service to deal with common data. A list has been defined inside the service along with some functions to transfer items from one list to another. The array size never seemed to change from 0,which is the logic i am trying to use in ngIf.
My logic was to check if the array is empty, then return a value of true if it was empty and set a variable empty in the controller. Then in ng-if I will check ng-if="b.empty" and thought that that would work but it didnt. The array size would remain 0 all throughout the life cycle of my code. I used ._isEmpty(list),angular([],[]) and the most obvious, array.length but the issue was initially they showed 0, but then the array size never changed. Even after populating the target array, the size seemed to stay 0 with any/all of the above functions/methods.
l1.$inject = ['listService']
function l1(listService){
var buying = this;
buying.items = listService.display();
buying.Add = function ($index){
listService.addItem($index);
}
}; //This is the controller for the source array.
.
.
.
bought.empty = listService.checkIfFull(); //Part of the second controller which assigns empty a boolean value
.
.
.
service.checkIfFull = function (){
if(blist.length == 0){
console.log(_.isEmpty(blist))
console.log(tblist)
return false;
}
else
{
console.log("not going here");
return true;
}
}; //The service checks if the array is empty
<div class="emptyMessage" ng-if="b.empty">Nothing bought</div>
The value of the console.log statements also only seem to be executing in the true portion of the if statement. I found a solution for this, which was to simply check in the html tag itself, if the local list's(that I'm looping through which ng-repeat)length was equal to zero and that worked. But could you please explain why my attempt is wrong? I am a beginner to AngularJs and JS in general so i might have not understood some rules about js and thus written wrong code. Thank you.
Edit: Here's the link to the codepen-> https://codepen.io/meanmanmachineman/pen/RmmdjY
Your problem is caused by the line bought.empty = listService.checkIfFull();. There you are calling to the function listService.checkIfFull() and assigning the returned value to bought.empty. What you should do is to assign the function itself to bought.empty:
bought.empty = listService.checkIfFull;
This way each time bought.empty is evaluated, it returns the current real value.
EDIT:
I'll try to be more explicit about the difference between, bought.empty = listService.checkIfFull() and bought.empty = listService.checkIfFull.
The first way, bought.empty will call to listService.checkIfFull() and store the returned value as a static value and any time the variable is evaluated, the value will be the same.
By using the other method, the value of bought.empty is not a number but the listService.checkIfFull function itself. This way, each time AngularJS evaluates the variable, the function is executed and returns the corresponding value.

Modifying scope object values in checkbox using ngchange

Forgive if this is a newbie error but I think this must be really simple and I am obviously missing something..
I have the following ngrepeat:
<div class="panel-body" data-ng-repeat="participant in activity.Participants" ng-show="showp" ng-init="participant.CheckInTime ='not set'"> <--The 'init' is for debug
{{getparticipantName(participant.ParticipantID)}}
Check in = {{participant.CheckInTime}}
<input type="checkbox" ng-model="participant.CheckedIn"
ng-change="setToNowOrNull(participant.CheckedIn, 'participant.CheckInTime')">
<br />
Check in = {{participant.CheckInTime}}
</div>
Which is nested within another ng-repeat which defines the controller etc. and that works fine. But when I click the checkbox, the settoNowOrNull function gets called, changes the value as it should, but this isn't returned to the participant.CheckInTime .. here is the function:
$scope.setToNowOrNull = function (deciderbool, thingtoset) {
if (deciderbool) //its been set to true.
{
$scope[thingtoset] = Date.now();
}
else //its been cleared to false
{
$scope[thingtoset] == null;
}
}
I added the $scope[thingtoset] after reading another question on here but to no avail.. the same with the single quotes around 'participant.CheckInTime' in the ng-change line. (This is supposed to pass the object not the value?)..
I'm obviously not getting something, and I'd have thought I could have done it in the html angular anyway rather than needing to call the controller for something so trivial - I just need to record the Date.Now() into the participant.CheckInTime. Thoughts anyone?
You can try this:
<input type="checkbox" ng-model="participant.CheckedIn"
ng-change="setToNowOrNull(participant)">
$scope.setToNowOrNull = function (participant) {
if (participant.CheckedIn) //its been set to true.
{
participant.CheckInTime = Date.now();
}
else //its been cleared to false
{
participant.CheckInTime == null;
}
}
There are several problems with your approach, particularly with
$scope[thingtoset] = Date.now();
Using value being passed into function this would be
$scope['participant.CheckInTime'] = Date.now();
This isn't the same as
$scope.participant.CheckInTime;
The whole string represents one object key and would have to be parsed into parts to get 2 levels out of it. Essentially it's invalid syntax for what you had hoped to accomplish
Even if it was valid there is no such object on your scope since participant is an alias for an object in the view. That object is within the array activity.Participants.
In conclusion, pass the actual object into your function and work with the whole object
If you want a generic method for multiple properties it would need to be more like:
$scope.setToNowOrNull = function (object, key){
switch(key){
case 'CheckInTime':
// code for this key
break;
}
}
Then in markup would use:
<input ng-change="setToNowOrNull(participant, 'CheckInTime')">
I suspect that you likely don't need such a generic method and can simply pass a single argument, the participant object, for that specific change handler
Within the ng-repeat, the object has its own scope, therefore when you try to write into $scope[thingtoset], instead of overriding the parent scope's corresponding object, you just create another object with the same name in the lower scope. Instead you can create an object in the upper scope, say $scope.thingHolder, and when this function is called, it can update $scope.thingHolder[thingtoset]. Then you will be able to observe the changes correctly in the upper scope too.

Unable to use variable in constructing $pop query in Meteor

I'm trying to use $pop on an embedded array in Meteor and I'm pretty certain that my limited understanding of variable scope and order of operations is causing me issues.
To provide a simplified example, this works:
do_thing = function(foo) {
Coll.update(foo, {$pop: { "bar.baz": 1 }} );
};
do_thing( "123" );
But this does not:
do_thing = function(foo, bar) {
var tmp = bar + ".baz"
Coll.update(foo, {$pop: { tmp: 1 }} );
};
do_thing( "123", "bar" );
The core issue is that the update succeeds when I hard-code the array from which I wish to pop the item, but the update fails when that array is dynamically constructed. I assume the cause is not the fact that it is a variable since 'foo' works, and instead the problem lies in the way that 'tmp' might not be getting instantiated in time for the update() call.
Can anyone suggest how I can achieve the result I'm looking for: popping an item from a field whose name I won't necessarily know until I'm in the function?
Alternately, I'm open to broader suggestions of how to construct this function if I'm taking a completely wrong-headed approach in the first place.
The literal string "tmp" is being used for the key name, rather than the value you are passing in for the variable "tmp".
Try this:
updateboj = {}
updateobj[tmp] = 1
Coll.update(foo, {$pop: updateobj} );

Variable Dependency with knockoutJS

I'm building an application with KnockoutJS with a component that essentially acts as a sequential spreadsheet. On different lines users may define variables or use them to represent a value.
So for example
x =2
x //2
x = 4
x //4
I have this working in the straightforward case of continuing adding new lines. The output function for each line checks and iterates backwards to see if the variable was ever defined previously. If it was it uses the first example it finds and sets that as the value. This works when initially defining the lines, and also works when you edit a line after a previous line has changed.
However, I would like variables to update if a previous definition of that variable has changed, been removed, or been added. That behavior does not exist right now. I have tried adding my own custom dependency handling code using a map to track the variables, but it badly impacted performance. I would like to tap into Knockouts dependency management to solve this, but I'm not sure of the best way to do so. Here is a brief summary of my code structure, I would be happy to add more detail if needed.
calcFramework is the view-model object I bind to the map. It consists of an observable list of Lines, a varMap, and other unrelated properties and functions
Line is a custom object. The relevant code is below
var Line = function (linenum,currline) {
var self = this;
self.varMap = {};
self.input = ko.observable("");
self.linenum = ko.observable(linenum);
self.lnOutput = ko.computed({
read:function(){
return outputFunction(self,self.input());
},
write:function(){},
owner:self
});
};
function outputFunction(self,input) {
try{
var out = EQParser.parse(input,10,self);
return out.toString();
}
catch(ex){
//error handling
}
}
Line.prototype.getVar = function (varName, notCurrentLine) {
if(typeof varName === "undefined"){
return null;
}
//Actually don't want ones set in the current varMap, only past lines
if(varName in this.varMap && notCurrentLine){
return this.varMap[varName];
}
if (this.linenum() > 0) {
var nextLine = calcFramework.lines()[this.linenum() - 1];
return nextLine.getVar(varName,true);
} else {
//eventually go to global
return calcFramework.varMap[varName];
}
};
Line.prototype.setVar = function(varName,value){
this.varMap[varName] = value;
};
SetVar and getVar are passed to eqParser, which gets the value of the expression, calling those functions as needed if a variable is referenced. So the variable value is not explicitly passed to the function and thus knockout does not view it as a dependency. But I'm not sure how I would pass the variable as a parameter without traversing the list every time.
So my question is, given this setup, what is the best way to track changes to a variable assignment (and/or new assignments) and update the lines that reference that variable, while maintaining good performance.
I recognize my question is lengthy and I have attempted to trim out all unnecessary detail. Thanks for your patience in reading.
I would be tempted to use a publish/subscribe model, using something like Peter Higgins' PubSub jquery plugin
Your overall app would subscribe/listen out for lines publishing an event that they have a variable definition. This would store any variable names in a standard javascript hashtable, along with the value. When a variable found event is published by a line, the app would check through all the known variables, and if it finds that it is a change to an existing variable value, it would publish a variable changed event. All the lines would subscribe to that event. They can then check whether they have a variable matching that name, and update the value accordingly.
Here's some untested code to give you an idea of what I mean:
var app = function()
{
var self = this;
self.variables = {};
$.subscribe('/variableAssigned', function (key, value)
{
// I think that this is the best way of checking that there is a variable
// in the object
if(self.variables.hasOwnProperty(key))
{
if(self.variables[key] !== value)
{
$.publish('/variableChanged', [ key, value ]);
}
}
});
}
In your Line object:
$.subscribe('/variableChanged', function (key, value)
{
// loop through varMap and see if any of them need updating.
});

Categories

Resources