This question already has an answer here:
Javascript closure not working
(1 answer)
Closed 5 years ago.
I'm trying to de-reference a property on a JavaScript object, but am not getting the expected result.
I have an array of Knockout view-models (I don't think the problem is Knockout-specific), each of which has an observable, Selected. I add a subscription to this observable so that a function, crossSelectTargetLangs is called when the value of Selected is changed.
Furthermore, I add this subscription inside a for... loop.
var tl = 0,
tlMax = allLangVMs.length,
vmLang,
selectedCode;
// for each 'vmLang' view-model in the 'allLangVMs' array...
for (; tl < tlMax; tl++) {
// local variable for the context view-model
vmLang = allLangVMs[tl];
// add the subscription to this observable
vmLang.Selected.subscribe(function() {
// de-reference the vmLang.Code property
selectedCode = (function(code) {
return code;
}(vmLang.Code));
// pass the de-ref'd value to the target function
crossSelectTargetLangs(selectedCode);
});
}
However, regardless of which view-model had its Selected observable updated, the argument passed to the target function is always the Code from the last element in the array, i.e. it doesn't appear to be de-referencing.
What am I doing wrong?
The problem is that you are making dereferencing in a wrong place.
The code should look like this:
var tl = 0,
tlMax = allLangVMs.length,
vmLang,
selectedCode;
// for each 'vmLang' view-model in the 'allLangVMs' array...
for (; tl < tlMax; tl++) {
// local variable for the context view-model
vmLang = allLangVMs[tl];
(function(vmLangParam) {
vmLangParam.Selected.subscribe(function() {
crossSelectTargetLangs(vmLangParam.Code);
});
})(vmLang);
}
Related
This question already has answers here:
Accessing an object property with a dynamically-computed name
(19 answers)
Closed 4 months ago.
This post was edited and submitted for review 4 months ago and failed to reopen the post:
Original close reason(s) were not resolved
I have a bunch of variables having boolean values. I wish to have another variable that stores the name of the boolean variable changed last. So, next time when a new boolean variable value is changed, I want to toggle the previous boolean variable.
Any idea/suggestion to achieve the above would be highly appreciated.
Eg. it would be something like this incorrect code-
isDemo1=false; isDemo2=false; isDemo3=true; isDemo4=false; isDemo5=false;
lastChangedBooleanVariable = this.isDemo3;
handleBooleanVaiables(currentChangedBooleanVariable: string)
{
// somehow toggle this.isDemo3 variable value
this.lastChangedBooleanVariable = currentChangedBooleanVariable;
// let's say currentChangedBooleanVariable = isDemo4
// somehow toggle this.isDemo4 value
}
Everytime that you need to do some weird code, rethink your approach, it will probably wrong.
You can instead use Arrays:
isDemo = [false, false, true, false, false];
lastChangedBooleanVariable = 3;
handleBooleanVaiables(currentChangedBooleanVariable: int) {
this.lastChangedBooleanVariable = currentChangedBooleanVariable;
isDemo[currentChangedBooleanVariable - 1] = !isDemo[currentChangedBooleanVariable - 1]
}
Sounds very much like what you're trying to do is functionally equivalent to:
let demo = 3;
Then set the value of this variable to any value from 1 to 5.
Instead of if (isDemo2), test if (demo == 2).
The "last changed variable" is always simply the current value demo holds, and that's probably obsolete information with this approach.
ES6 has a feature on Arrays called fill which can be pretty handy here. Essentially, you just set all the values to false before you activate a new version.
let demos = new Array(5).fill(false);
function activateDemo(demo) {
demos = new Array(5).fill(false);
demos[demo] = true;
}
activateDemo(4);
let demos = new Array(5).fill(false);
function activateDemo(demo) {
const humanIndex = demo > 0 ? demo - 1 : 0; // to make it into human counting form, if you want.
demos = new Array(5).fill(false);
demos[humanIndex] = true;
}
activateDemo(4);
console.log("4", demos);
activateDemo(3);
console.log("3", demos);
var keys = {
"isDemo1":false, "isDemo2":false, "isDemo3":false, "isDemo4":false, "isDemo5":false;
};
var lastChangedBooleanVariable: string = "isDemo3";
const nameOf = (f) => (f).toString().replace(/[ |\(\)=>]/g,'');
function getName(varName:string){
return varName.substring(
varName.indexOf(".") + 1,
varName.lastIndexOf(";")
);
}
function handleBooleanVaiables(currentChangedBooleanVariable: string)
{
console.log(keys[lastChangedBooleanVariable]);
keys[lastChangedBooleanVariable] = !keys[lastChangedBooleanVariable];
keys[currentChangedBooleanVariable] = ! keys[currentChangedBooleanVariable];
lastChangedBooleanVariable = currentChangedBooleanVariable;
// let's say currentChangedBooleanVariable = isDemo4
// somehow toggle this.isDemo4 value
}
function printValues(){
console.log(keys);
}
var varName = getName(nameOf(()=>keys.isDemo1));
handleBooleanVaiables(varName);
printValues();
varName = getName(nameOf(()=>keys.isDemo4));
handleBooleanVaiables(varName);
printValues();
here's fiddle: https://jsfiddle.net/hjv540tk/
This question already has answers here:
Accessing an object property with a dynamically-computed name
(19 answers)
Closed last year.
5 weeks into learning JavaScript.
I have the following scenario. I created a custom object/class Question with 5 properties:
constructor class
class Question {
constructor(interrogative, answRight, answWrong1, answWrong2, answWrong3) {
this.interrogative = interrogative;
this.answRight = answRight;
this.answWrong1 = answWrong1;
this.answWrong2 = answWrong2;
this.answWrong3 = answWrong3;
}
}
I have an array of objects Question which I populate by reading some text from a file. The text from the file is passed as values for the properties of my Question object. The name of the array that holds these Question objects is called quizQuestionsArray. No problem there so far.
Now I want to look only at the first object I have in my quizQuestionsArray. So, quizQuestionsArray[0]. That object looks like: object in quizQuestionsArray[0]
I want to select at random one of its properties whether it's answRight, answWrong1, answWrong2, or answWrong3 and assign the value of that randomly-selected property to an HTML's < li > innerText property. Once I assign that property value, I would in theory eliminate that value from propertiesArray (commented out momentarily until current issue is resolved) and then continue this loop up to 4 times (the total number of properties in object class that need to be assigned randomly to distinct HTML < li > elements; property interrogative is assigned earlier in the code so it does not enter into importance here). See the picture below for reference.Snapshot of code which is not giving me what I want
let i = 0;
var propertiesArray = ["answRight", "answWrong1", "answWrong2", "answWrong3"];
debugger;
// randomly assign value of properties to the current choice list item
for (let j = 0; j < 4; j++) {
someRandomNum = randomNumber(0, propertiesArray.length-1);
// this conditional will always run
if (someRandomNum != null) {
choiceListItem = document.createElement("li");
currObj = quizQuestionsArray[i];
currProperty = propertiesArray[someRandomNum]
console.log(currObj, currProperty);
choiceListItem.innerText = currObj.currProperty;
// propertiesArray = propertiesArray.splice(someRandomNum,1);
choiceList.appendChild(choiceListItem);
}
}
// append the choice list to the div wrapper
container.appendChild(choiceList);
It's notable that the execution of my script does not actually throw an error. When execution finishes, the HTML < li > element innerText value comes out as undefined, which is clearly none of the values of the object located in quizQuestionsArray[0]. snapshot of HTML < li > element showing undefined as its value
How can I go about resolving this? Your help and guidance is appreciated!
Change how you are accessing a property dynamically
In your loop you have the following
currProperty = propertiesArray[someRandomNum]
console.log(currObj, currProperty);
choiceListItem.innerText = currObj.currProperty;
On that third line when you call currObj.currProperty it doesn't reference the value of the variable currProperty. It assume there is a property on that object called currProperty. When you need to access dynamic properties in JS you use the square braces, []. Rewriting the above code should work as follows:
currProperty = propertiesArray[someRandomNum]
console.log(currObj, currProperty);
choiceListItem.innerText = currObj[currProperty];
Example:
var home = {
color: "blue",
occupied: true,
property: "apartment"
}
var property = "color";
console.log("Home.Property: ", home.property)
console.log("Home[Property]: ", home[property])
I am currently stuck with a javaScript for loop.
The situation is like this, in my program there is a function which returns true/ false value in every 200 ms.
The function, which I am currently trying to code, should obtain the value from the above function ( for the ease of reference, I would name it as, function1) and store it in an array.
I am trying to use a for loop to store those values in an 8 element array.
Shown below is my code,
function myFunction1(imagestatus) //
{
var statusArray = ["","","","","","","",""];
for (var i = 0; i <= statusArray.length - 1; i++)
{
statusArray[i] = imagestatus;
}
}
Now, during the first execution of the for loop, it will assign the 0th element of the array, true or false. And during the second execution also it will do the same which is no good.
The task I expect to do is, when the function1 returns its value to myFunction it must store it in the 0th element. Then when it returns again, if its as same as the value in 0th element, store it in the 1st element, if not same, then take a different action.
Start with an empty array:
var array[];
then use:
array.push(data);
To add each datum to the right end of the array.
Sounds like a number of things need to happen. First, you are asking for a callback function. Second, you need to move the status array to the global scope.
var statusArray = ["","","","","","","",""];
function myFunction1(imagestatus, callback, differentAction) //
{
var i = 0;
// if its as same as the value in 0th element,
while (statusArray[i]==imagestatus)
{
i++;
}
if (i<statusArray.length && i>0)
{
// store it in the 1st element
statusArray[i]=imagestatus;
if (typeof(callback)=="function")
{
callback();
return;
}
}
// if not same, then take a different action
if (typeof(differentAction)=="function")
{
differentAction();
return;
}
}
This question already has answers here:
Javascript multiple dynamic addEventListener created in for loop - passing parameters not working
(5 answers)
Closed 7 years ago.
Basically I created a bunch of cells and I am trying to add onclick to each one passing a variable to use inside the function. But doing straight up passes the 'i' variable as the last i value and not 0,1,2,3,4 etc. Here is a snippet of what I am doing.
for (var i = 0; i < cellCount.length; i++) {
var cellName= "cell"+ i
document.getElementById(cellName).onclick = function () { cellClicked(i) };
}
If you do not "capture" the value in a new scope, the callback will read the value from the actual i-counter.
Do something like this:
for (var i = 0; i < cellCount.length; i++) {
(function(copy_of_i) {
var cellName= "cell"+ i
document.getElementById(cellName).onclick = function () { cellClicked(copy_of_i) };
})(i)
}
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
I'm making a form where the user can RSVP for themselves and up to 5 others. Each guest has a text field for their names and then radio buttons (yes or no) as to whether they are attending or not. By default Guest 1 & 2 are displayed and then there is a button the user can click to add another guest up until guest #6.
Guest's 3 - 6 are contained in their own objects like this:
var guestThree = {
name : document.querySelector('label[for="nameOfThirdGuest"]'),
attendingYes : document.querySelector('label[for="guestThreeAttendingYes"]'),
attendingNo : document.querySelector('label[for="guestThreeAttendingNo"]')
};
var guestFour = {
name : document.querySelector('label[for="nameOfFourthGuest"]'),
attendingYes : document.querySelector('label[for="guestFourAttendingYes"]'),
attendingNo : document.querySelector('label[for="guestFourAttendingNo"]')
};
var guestFive = {
name : document.querySelector('label[for="nameOfFifthGuest"]'),
attendingYes : document.querySelector('label[for="guestFiveAttendingYes"]'),
attendingNo : document.querySelector('label[for="guestFiveAttendingNo"]')
};
var guestSix = {
name : document.querySelector('label[for="nameOfSixthGuest"]'),
attendingYes : document.querySelector('label[for="guestSixAttendingYes"]'),
attendingNo : document.querySelector('label[for="guestSixAttendingNo"]')
};
I am hiding guest's 3 - 6 using the following function which are then converted to methods for the above objects:
function hideFields() {
this.name.style.display = "none";
this.attendingYes.style.display = "none";
this.attendingNo.style.display = "none";
}
I also have the opposite of the above function in showFields:
function showFields() {
this.name.style.display = "block";
this.attendingYes.style.display = "block";
this.attendingNo.style.display = "block";
}
So the idea here is that when the add guest button (which is assigned to the variable addGuest) is clicked guest #3 will be added. Then when it's clicked again guest #4 will be added and so on until #6. I've added the guestThree - guestSix objects to the following array:
var extraGuests = [guestThree, guestFour, guestFive, guestSix];
and then using the following for loop:
for (var guest = 0; guest < extraGuests.length; guest++) {
addGuest.onclick = function() {
extraGuests[guest].showFields();
};
}
But this doesn't work. I have discovered through testing that guest loops all the way round to 4 without executing the showFields methods. If I was to add a break after the onclick event then guestThree will display but that's it (obviously because that's how break works) but this atleast shows that the loop works as I hoped - to an extent.
So what I am asking of you guys is - what am I doing wrong? How do I make it so that the guest variable doesn't loop straight to 4? I need each iteration to be 0, 1, 2 and finally 3 and as far as I can see that is exactly what should be happening?
Cheers!
There are two problems:
The first is that you're overwriting the onclick on addGuest on every loop, so only the last assignment survives. So if we fix the second problem (below), you'll still only show the last set of fields. Probably, we want the handler assigned once, and have it reveal the "next" set of fields.
The second problem is that the function you're assigning to onclick has an enduring reference to the guest variable, not a copy of it as of when the function was created. So later, when the function gets called, it sees the value guest has after the end of the for loop. Your main choices for solving that are a builder function, and Function#bind as described under this question. But since we don't need or want the loop, it doesn't matter.
Instead, I'd suggest a variable telling you what the next set of fields to reveal is, and then simply one function that uses it:
var extraGuests = [guestThree, guestFour, guestFive, guestSix];
var nextGuest = 0;
addGuest.onclick = function() {
var guest = extraGuests[nextGuest];
if (guest) {
++nextGuest;
guest.showFields();
}
};
You've closed over a loop variable, which means that at the time they are called, your click handlers are all looking at a single value of guest, which will be extraGuests.length (the value it settles on when the loop is complete).
You need to capture the loop variable at the time the loop executes, which can be done easily using an Immediately-invoked function expression. :
for (var guest = 0; guest < extraGuests.length; guest++) {
(function(ind){
addGuest.onclick = function() {
extraGuests[ind].showFields();
};
})(guest);
}
There's probably better/more succinct ways to do this using newer language features.
Maybe this.
var guest = 0;
addGuest.addEventListener('click', function() {
if(guest < extraGuests.length)
extraGuests[++guest].showFields();
}, false);