Angularjs update scope inside iteration - javascript

In the following code i'm trying to get subquestions list for each question and show them in the view ,but i have problem in updating subquestion for each question,it don't update so I get same subquestion list for all question.
How can I show each question and list all subquestion under it?
function loadAllQuestionGroup () {
Questiongroup.query({}).$promise.then(function(group){
vm.questiongroups = group;
for(var i=0;i<group.length;i++){
var grouptitle=group[i].title
Question.questionsByQuestionGroup({id:group[i].id}).$promise.then(function(question){
vm.question = question;
for(var j=0;j<question.length;j++){
Subquestion.subquestionsByQuestion({id:question[j].id}).$promise.then(function(subquestion){
vm.subquestions=subquestion;
});
}
});
}
});
}
<div ng-repeat="question in vm.question">
{{question.name}}
<div ng-repeat="subquestion in vm.subquestions">
{{subquestion.name}}
</div>
</div>

Looks like it's not an issue related with angular scope, it's pure javascript scope that you have trouble with. If you making asynchronous calls in the loop, you will receive each time the last element. To fix it, you should wrap promises inside loop with anonymous functions.
Check this classic example(you can think that setTimeout function is equivalent of your Question.questionsByQuestionGroup({id:group[i].id}).$promise, they are both async functions)
//THIS IS EQUIVALENT TO YOUR CASE
//In this case it will return each time last element, 5 in this case
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i)
});
}
//Result: 5 5 5 5 5
//THIS IS HOW YOU SHOULD REFACTOR
//Make a wrapper for async, and it will work as needed
for (var j = 0; j < 5; j++) {
(function(index) {
setTimeout(function() {
console.log(index)
});
})(j);
}
//Result: 0 1 2 3 4
P.S. After you will fix it with wrappers, your code will be even more unreadable, better to refactor it to separate functions.

Individual questions need to be pushed to a question list. Create a subquestion list for each individual question. Individual subquestions need to be pushed to their respective lists.
function loadAllQuestionGroup () {
Questiongroup.query({}).$promise.then(function(group){
vm.questiongroups = group;
//Initialize question list
vm.questionList = [];
for (var i=0;i<group.length;i++){
var grouptitle=group[i].title;
Question
.questionsByQuestionGroup({id:group[i].id})
.$promise
.then(function(question){
//Push question to list
vm.questionList.push(question);
for (var j=0;j<question.length;j++) {
//Initialize subquestion list
question.subquestionList = [];
Subquestion
.subquestionsByQuestion({id:question[j].id})
.$promise
.then(function(subquestion){
//Push subquestion to list
question.subquestionList.push(subquestion);
})
;
};
})
;
}
});
}
Subquestion interation (ng-repeat) should be done on the question iterator.
<div ng-repeat="question in vm.questionList">
{{question.name}}
<div ng-repeat="subquestion in question.subquestionList">
{{subquestion.name}}
</div>
</div>
Notice that a subquestionList property was added to each question in the questionList.

Related

Add onclick event to array items [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I'm trying to iterate over an array and apply an onclick event to each item. I'm hoping to be able to click each of my divs and have them console log their value. Right now I'm stuck on how to apply the onclick to each div. I'm new to JS, so I'm not completely of why I shouldn't make a function inside of a loop like JSBin is complaining about. I've messed around with a lot of different ways to do this, but am truly stuck...
JSBin
function numberTrack() {
var gridItems = document.getElementsByClassName("grid");
for (var i = 0; i < gridItems[0].length; i ++) {
gridItems.onclick = function(){
alert("hello");
};
}
}
numberTrack();
var c = document.getElementsByClassName("divs"); <--- array of divs
for (var i = 0; i < c.length; i++) {
c[i].onclick = function() {
console.log(this.value);
}
}
JSBin is complaining because it wants you to declare the function outside the for loop and then assign it inside the for loop. This is more efficient then what you are currently doing, which is assigning a new anonymous function to each item in the array. All those identical functions will have to be created and stored separately in memory.
You can do something like this instead:
function alertHello() {
alert("hello");
}
for (var i = 0; i < gridItems.length; i++) {
gridItems[i].onclick = alertHello;
}
You need to loop through all the items in the gridItems collection, And inside the loop, get each item using the iterator i value.
function handleClick()
{
alert("hello");
}
function numberTrack() {
var gridItems = document.getElementsByClassName("grid");
for (var i = 0; i < gridItems.length; i ++) {
gridItems[i].onclick = handleClick;
}
}
numberTrack();
If you are allowed to use jQuery, you can bind the event to items like this.
$(function(){
$(document).on("click",".grid",function(e){
var item =$(this);
alert(item.html())
})
})
Here is a working jsBin sample

Loop and Functions

I'm having troubles gathering information about clicked eventListeners.
I have this loop which builds an array:
myButtonList = document.getElementsByTagName('a');
myAnchorList = [];
for (i=0; i < myButtonList.length;i++) {
if (myButtonList[i].getAttribute('class') == 'flagged') {
myAnchorList.push(myButtonList[i]);
}
}
For each <a> put into myAnchorList array, I also create another array storing other informations from the same tag (classe and other atrributes).
Here's where I'm struggling. I'm trying to set up an eventListener to send me back those information when those <a> are being clicked. But somehow, the fact that I create a function (for the eventListener) within a loop breaks everything.
for (i=0; i < myAnchorList.length; i++) {
myAnchorList[i].addEventListener("click", function(i){
console.log(alpha+' - '+beta[i]+" - "+charlie[i]);
});
}
My values will either be undefined or some other values which will be the same for each buttons I clicked. alpha is working well as it doesn't depend on any iteration of the loop, but not the others.
Can anybody see what I'm doing wrong here?
for (var i = 0; i < myAnchorList.length; i++) {
(function (i) { //Passes i to your function
myAnchorList[i].addEventListener("click", function () {
console.log(alpha+' - '+beta[i]+" - "+charlie[i]);
});
})(i);
}
The variable "i" in closure that you created in the loop will always retrieve the last value(myAnchorList.length - 1). You shouldn't create closure in a loop, and you can use a "factory" method to create closure instead.

How to alert numbers in a for loop [duplicate]

This question already has answers here:
How do JavaScript closures work?
(86 answers)
Looping setTimeout
(3 answers)
Closed 8 years ago.
Please help me fix this code
HTML
<p class="imgclass">Test1</p>
<p class="imgclass">Test2</p>
<p class="imgclass">Test3</p>
JavaScript
imgclassElements = document.getElementsByClassName('imgclass');
for (var i = 0; i < imgclassElements.length; i++){
imgclassElements[i].onmouseover=function(){alert(i);};
}
In browser onmouseover elements with class .imgclass appear alertbox only with text "3". How fix this? I want to pop-up alert window with values appropriate .imgclass elements order.
This is the simplest way to do it, you can use a closure but I'll keep it simple. Also you can access the element and apply styles, or get info from it in the click handler
FIDDLE
http://jsfiddle.net/bz7a2vf5/1/
HTML
<p id="item1" class="imgclass">Test1</p>
<p id="item2" class="imgclass">Test2</p>
<p id="item3" class="imgclass">Test3</p>
JS
imgclassElements = document.getElementsByClassName('imgclass');
for (var i = 0; i < imgclassElements.length; i++) {
click(i);
}
function click(i){
imgclassElements[i].onmouseover = function (e) {
// you can also get the element from e.target
alert(i);
alert(e.target.id);
};
}
This may not be the most elegant way, but
imgclassElements[i].addEventListener('mouseover',function(j){return function(){alert(j);};}(i));
should do it.
That said, you should really read up on JS closures as suggested in the comments to your question.
That problem is occurring because of the time alert is being called, value of i is incremented to 3 that's why it's giving 3 every time.
Try to bind a function with the current value of i like bellow
imgclassElements = document.getElementsByClassName('imgclass');
var printVal = function(i){
return function(){
alert(i);
};
}
for (var i = 0; i < imgclassElements.length; i++){
imgclassElements[i].onmouseover = printVal(i)
}
There can be better solutions also.
The easiest way is to extract the content of your loop in a function
function handleElement(element, i) {
element.onmouseover=function(){alert(i);};
};
for (var i = 0; i < imgclassElements.length; i++){
handleElement(imgclassElements[i], i);
}
Or you can use forEach (Sadly it's a HTMLCollection and not an Array, so it's a bit difficult)
Array.prototype.forEach.call(imgclassElements, function(element, i) {
element.onmouseover=function(){alert(i);};
});
Or give the index as an argument
for (var i = 0; i < imgclassElements.length; i++){
imgclassElements[i].onmouseover=(function(i){alert(i);}).bind(null, i);
}
And always a good thing to learn about closures

Trouble Returning Value of Clicked Element Javascript

I'm trying my hand at Javascript and interacting with the DOM for the first time by making a simple quiz game. All of my elements are generated dynamically by JS except a few divs.
so here is my problem.
I have
Question 1.
(A)
(B)
(C)
(D)
I want to be able to click on any of the answers and have the values returned. To do that, I wrote the function
function checkCorrectness(element){
element.onclick = function(){
nodes = element.childNodes
for(i = 0; i<nodes.length; i++){
nodes[i].onclick = function(){console.log(nodes)};
}
}
}
//Note answers selectsthe div containing the 4 <p> elements A,B,C,D
checkCorrectness(answers)
Which returns me, as expected, an array of the four elements containing my answers. So, I thought the logical next step would be to select the particular node onClick by changing it by console.log-ing nodes[i] instead of nodes. I would expect this to return me the element which I clicked on, so I could compare its inner HTML to the correct answer, therefore seeing if it was the right answer.
function checkCorrectness(element){
element.onclick = function(){
nodes = element.childNodes
for(i = 0; i<nodes.length; i++){
nodes[i].onclick = function(){console.log(nodes[i])};
}
}
}
checkCorrectness(answers)
However, it just returns undefined. Any help would be much appreciated!
Ah, you've discovered JavaScript closures (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures). This is a very common mistake for those new to JS, so don't feel bad. When you log nodes[i], you're actually accessing nodes at the index for the most recent value of "i" at the time the function executes. Which, in this case, is nodes.length, which is undefined... sorry if that doesn't make sense, but check out the linked article.
You really want something like this:
var logNode = function(val) {
return function() { console.log(nodes[val]) };
};
for(i = 0; i<nodes.length; i++){
nodes[i].onclick = logNode(i);
}
The whole logic of your quiz may be written in a couple of lines
// JavaScript
window.onload = function(){
// This is a simple structure to hold your question_id and the correct answer
// var data = {'10':'B', '11':'D', '12':'A', '13':'A'}; ...
// for this exampple we'll have only one
var data = {'10':'B'};
var li = document.getElementsByTagName('UL')[0].children;
for(i = 0; i < li.length; i++){
li[i].onclick = function(){
if(data[this.parentNode.id] == this.innerHTML){
alert(this.innerHTML + " -> Correct");
}else{
alert('Nope!');
}
};
}
};
// HTML
// Let's say every question has an `id`
// I'd use a list for this case but you may use any other markup
<ul id="10">
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
</ul>
Working jsBin

Array iteration issue in Javascript

I´m trying to make a function that reads elements from an array and distributes its values to <tr> tags of a table.
So I wrote this code:
(function () {
"use strict";
var selectQG = {
distributeCat : function (tablerow, categories) {
var tr = $(tablerow), tri = 0;
tr.each(function () {
if (tri > 2) {
for (var i = 0; i<categories.length; i++) {
this.setAttribute('categoria',categories[i]);
}
}
tri++;
});
}
}
var categories = ['1,2','3,4','5,6','7,8','9,10','11,12'];
selectQG.distributeCat('table tr', categories);
} () );
Check this fiddle to see the html code and the function working: http://jsfiddle.net/5kqEf/2/ )
The loop is being executed 6 times and I can´t figure out why. I´m still a novice at programming, so I´m in need of some help to know what I´m doing wrong. Any help would be very appreciated.
EDIT: The ideia is that every <tr> get the array value. It is supposed to happen starting from the 3rd <tr>, that´s why I put that "if" conditional. So, the first <tr> get "categories[1]", the second <tr> get "categories[2]" and so on.
The outer loop is executed 9 times, since you have 9 table tr elements. The .each will loop over every tr element, which happen to be 9 elements. See http://jsfiddle.net/5kqEf/4/
The inner for loop loops over 6 elements (the array var categories = ['1,2','3,4','5,6','7,8','9,10','11,12'];), so that makes sense...
What is your exact question?
If you want to pick a categorie based on index (and startover if there are more tr elements than categories), you might want to replace this:
for (var i = 0; i<categories.length; i++) {
this.setAttribute('categoria',categories[i]);
}
with this
var index = tri % categories.length;
this.setAttribute('categoria',categories[index]);
See https://developer.mozilla.org/en/JavaScript/Reference/Operators/Arithmetic_Operators for use of the modulus operator.

Categories