Ember control does not come back to loop after sending action - javascript

In my Ember app, I have the following code to remove checked rows dynamically
removeRow: function(row){
// Some logic to remove one row at a time
var numberContainers = this.get('containers').length;
for (var i = 0; i < numberContainers; i++){
}
}
this.get('containers').forEach(function(container){
if (container.applicable === true){
var row = {};
self.send("removeRow", row);
}
})
Now user can select multiple rows & try removing them. With the above code, the "removeRow" action is invoked only once i.e. the forEach loop somehow gets broken or control does not come back again after the "removeRow" action is invoked once.
How can I handle this scenario ?

A couple of things were mentioned in comments. You have some different smells and errors:
While you're iterating on an array, you are trying to modify it.
Instead of calling a function of the component, you are sending an action to call it.
It is not clear var row = {}; self.send("removeRow", row); If a container is suitable why we are removing a newly created row object?
Anyway, my suggestions are:
Separate the array modification and iteration
Define removeRow as a function, if you need to use it as an action handler also define an action handler and delegate the whole responsibilty to the function.
Here is a sample code:
removeRow: function(row){
// Some logic to remove one row at a time
var numberContainers = this.get('containers').length;
for (var i = 0; i < numberContainers; i++){
}
}
otherFunction(){
let applicables = this.get('containers').filterBy('applicable', true);
applicables.forEach(a=>{let row={};this.removeRow(row);});
}
actions:{
removeRow(row){
this.removeRow(row);
}
}

Related

Displaying returned list from a function which loops through a viewModel

I've got a function that takes a container and loops through its objects to perform a search and retrieve an array of the found items.
The ViewModel has a container passed like such e.g.:
function ShowRoomBuildingsViewModel() {
this.LoadModel = function (container) {
for (var u in container.Payload.ShowRoom) {
if (container.Payload.ShowRoom[u].Inventory!== undefined)
this.Content.push(container.Payload.ShowRoom[u].HVehicle, container.Payload.ShowRoom[u].Cars);
}
// Definitions
this.Inventory= ko.observableDictionary();
this.Content= ko.observableArray();
I'm trying to create an ObservableArray object to loop through in my front end using <!-- ko foreach -->. The below code should retrieve either one result or multiple.
this.Content = function (container) {
var concatedList;
Content.forEach(element, elementIndex => {
var Content1 = container.Payload.Inventory.HVehicle.find(el => el.ID == element.ID);
if (Content1 != undefined) {
concatedList[elementIndex] = element;
concatedList[elementIndex].push(Content1);
}
});
return concatedList;
};
I've tried putting breakpoints to see what is happening inside, but it doesn't go in. I'm not sure if this is the correct approach or if it even works, but it is not showing any errors. In the first loop, where it populates Content, I can see that there are five results.
Any suggestions would be much appreciated.

getElementsByClassName onclick and return data

I have many these links in my template
Add
And need click to the link and get certain data-slug. I try use JS and get all links so.
var add_to_cart = document.getElementsByClassName('add_to_cart');
After I try for
for(var i = 0; i < add_to_cart.length; i++) {
product_slug = add_to_cart[i].getAttribute('data-slug')
add_to_cart[i].onclick = function() {
console.log(product_slug)
}
}
And after click always console.log have last data-slug latest link in my template. How to fix it and get the data-slug of the item I clicked on. Help me, please.
Your for loop does not work because the value of product_slug is set when the loop runs.
Because product_slug is a var you overwrite it each time the loop runs. And when the click listener is called product_slug will have the same value it had when the loop ran for the last time.
var creates a variable with function scope, it is defined in the scope of the function.
let or const creates a variable with block scope, it is defined in the scope of the block (inside the curly braces { }).
Read more about scope.
So you want to use let.
for(var i = 0; i < add_to_cart.length; i++) {
let product_slug = add_to_cart[i].getAttribute('data-slug')
add_to_cart[i].onclick = function() {
console.log(product_slug)
}
}
You are defining a variable that is written n times during a for loop, and n events that will display the value. That's why you have the same output each time.
What you can do is get the current target of your onclick event, and then display it's data-slug attribute
your for loop would look like this :
var add_to_cart = document.getElementsByClassName('add_to_cart');
for(var i = 0; i < add_to_cart.length; i++) {
add_to_cart[i].onclick = function(evt) {
console.log(evt.target.getAttribute('data-slug'));
}
}
You want to use the target of the click. That's the .target attribute of the first argument to the event handler.
For example, the following code assigns an event handler to all the add_to_cart elements on the page and logs the slug to the console when they're clicked:
let add_to_cart_buttons = document.querySelectorAll('.add_to_cart');
add_to_cart_buttons.forEach(function(node) {
node.addEventListener('click', (e) => console.log(e.target.dataset.slug));
});
EDIT: Apologies, I misread your second code block when I first wrote this. Your use of document.getElementsByClassName is correct.

How to do JS variable substitution for exportTableToCSV?

In my web page I want to be able to export multiple tables as csv. I can code these individually (like this), but my for loop isn't working.
// this works
$("#xx001").on('click', function (event) { exportTableToCSV.apply(this, [$('#table001'), 'export.csv']); });
$("#xx002").on('click', function (event) { exportTableToCSV.apply(this, [$('#table002'), 'export.csv']); });
// this fails
let myxx = "";
let mytable = "";
for (let i = 0; i < 5; i++) {
myxx += "xx00" + i ;
table += "table00" + i ;
$('#'+${myxx}).on('click', function (event) { exportTableToCSV.apply(this, [$( '#'+${table} ), 'export.csv']); });
});
I expected that the both tables could be exported, but I'm getting a "File not found" error using the loop.
Your function inside of the loop will call that last myxx and table which both are 005 at the time of firing event ( when the loop is iterated until the end, and those two variables got filled with the last value of loop ).
If you need more explanation about what dafuq is happening, you should check these articles about closures. article one and article two. There are tons of more resources out there, but I just found these two at the moment.
You need to pass the right variable to it just like this:
for (let i = 0; i < 5; i++) {
let myxx = "xx00" + i ;
let table = "table00" + i ;
(function( x, t ) {
$('#' + x ).on('click', function (event) { exportTableToCSV.apply(this, [$( '#' + t ), 'export.csv']); });
})( myxx, table );
};
the above code should do the job for you.
It shouldn't be necessary to create a loop for this. A single jQuery statement can bind a listener to every button.
$('.btn').on('click', function (event)...
The trick is that apply() needs the id of the relevant table. That can be provided dynamically by traversing the DOM relative to the particular button that was pressed.
If the table element is followed by the button (as it is in the linked example), then we only need to reference the previous element:
$('.btn').on('click', function (event) { exportTableToCSV.apply(this, [$(this).prev().attr("id"), 'export.csv'])
});
Note that prev() can accept selector arguments, so even if the table isn't the immediately preceding sibling element you could use a selector to find it something like prev("table")

How to add array to parameter with function?

I am very new to programming and I am wondering if anyone can help me with this.
I am trying to make a pop up page.
I set variables for each click area which I set each area with div and placed with css.
Also for each pop up image which I put div id on each image on html and set display = "none" on css.
I want to make a function that shows one image on touchend and hide other images at the same time.
Could you help me with my code?
var pop = new Array("pop1","pop2","pop3","pop4","pop5","pop6");
var clickArea = new Array("click1","click2","click3","click4","click5","click6");
function diplay(click,show,hide){
click.addEventListner("touchend",function(){
show.style.display = "block";
hide.style.display = "none";
});
};
display("click[0]","pop[0]","pop[1,2,3,4,5]");
There are a few different issues with your code.
You used strings instead of the actual code structure references while calling display. I see that you mean for these to reference the element ids, but you must first get the element with document.getElementById(...) or jQuery's $("#...").
In the pop and clickArea arrays, you used strings, which do not have the .style object. You need to reference the elements themselves.
Your code structure is not designed to handle arrays.
You need to define the addEventListener before you need the function handler to be called. You do not want this every time.
The click argument in the display function is redundant, as it is never called.
You are using jQuery. You should have stated this! (but you're forgiven) :)
You can't reach into arrays with the syntax arrayName[#,#,#].
You misspelled "display". Whoops!
The arrays are redundant, since the code needed to be restructured.
First, in order to address Point #4, we need this code to run when the DOM has finished loading:
var clickArea = new Array("click1","click2","click3","click4","click5","click6");
clickArea.each(function(id){
$("#"+id)[0].addEventListener("touchend", display);
});
Next, we need to fix the issues with your code. They're explained above.
var pop = new Array("pop1","pop2","pop3","pop4","pop5","pop6");
function display(event){
var indx = Number(event.target.id.split(/\D/i).join(""));
$("#pop"+indx)[0].style.display = "block";
pop.each(function(ide) {
if (ide.split(/\D/i).join("") != indx-1) {
$("#"+ide)[0].style.display = "none";
}
});
};
Otherwise, great job! All of us started out like this, and believe in you! Keep it up!
P.S. You can set arrays like this [ ? , ? , ? , ? ] instead of this new Array( ? , ? , ? , ? ).
Here is an example using for loops instead of methods of Arrays etc
Start off by defining everything you can
var popup_id = ["pop1", "pop2", "pop3", "pop4", "pop5", "pop6"],
popup_elm = [], // for referencing the elements later
area_id = ["click1", "click2", "click3", "click4", "click5", "click6"],
area_elm = [], // for referencing the elements later
i; // for the for -- don't forget to var everything you use
// a function to hide all popups
function hideAll() {
var i; // it's own var means it doesn't change anything outside the function
for (i = 0; i < popup_elm.length; ++i) {
popup_elm.style.display = 'none';
}
}
// a function to attach listeners
function listenTouch(area, popup) {
area.addEventListener('touchend', function () {
hideAll();
popup.style.display = 'block';
});
// we did this in it's own function to give us a "closure"
}
Finally we are ready do begin linking it all to the DOM, I'm assuming the following code is executed after the elements exist in the browser
// setup - get Elements from ids, attach listeners
for (i = 0; i < popup_id.length; ++i) {
popup_elm[i] = document.getElementById(popup_id[i]);
area_elm[i] = document.getElementById(area_id[i]);
listenTouch(area_elm[i], popup_elm[i]);
}
You cannot treat strings as html elements.
Assuming there are elements with click area ids in the page, you may do something like (once the document is ready).
var popEls = pop.map(function (id) { return document.getElementById(id) });
clickArea.forEach(function (id) {
var clickAreaEl = document.getElementById(id);
clickAreaEl.addEventListener('click', onClickAreaClick);
});
function onClickAreaClick() {
var clickAreaNum = +this.id.match(/\d+$/)[0],
popIndex = clickAreaNum - 1;
popEls.forEach(function (popEl) {
popEl.style.display = 'none';
});
popEls[popIndex].style.display = 'block';
}

Java Script Function is not Defined JSP Error

I keep getting "function is not defined" errors while working on a Javascript/HTML page.
EDIT: Updated Link: http://jsfiddle.net/Gmyag/134/
EDIT: Updated link http://puu.sh/8CxnC/b954c1c803.html is the actual one I'm working with and would likely prove leagues more useful than the fiddle.
HTML:
deliveryIdentification is the one giving issues. Code too long.
Had to add code block since I added a jsfiddle.
Sorry for not simplifying the example, but this is the first time I've seen this.
If I put everything on a separate script blocks others seem to work, but with the addDelRefOrder() since I need to declare var deliveryDummy[] before it throws "ReferenceError: deliveryDummy is not defined. And if I put deliveryDummy[] in the same block it says "ReferenceError:addDelRefOrder() is not defined".
As to why the structure is so weird it's due to it being a .jsp file. I'm just starting out with JSP and learning a lot along the way.
Any and all help as of how to fix this issue is greatly appreciated.
You are defining function inside function ? Here
function renderList()
{
// clean the list:
while (list.firstChild) {
list.removeChild(list.firstChild);
}
// Recreate li
for(var i = 0; i < deliveryDummy.length; i++) {
var entry = document.createElement("li");
entry.appendChild(document.createTextNode(deliveryDummy[i]));
var removeButton = document.createElement('button');
removeButton.appendChild(document.createTextNode("Remove"));
removeButton.setAttribute('onClick','removeName('+i+')');
entry.appendChild(removeButton);
list.appendChild(entry);
}
function removeDeliver(deliverIndex){
deliverDummy.splice(deliverIndex,1);
// Array changed to re-render List
renderList();
}
function getDeliver() {
return deliverDummy;
}
}
you have renderList() and inside this two more funcs. This is the wrong structure for Javascript. Make separate functions.
I'm not very familiar with jsp either but your HTML tagging is a little messy.
Make sure that the html tags are properly nested.
in your javascript i noticed that you have made calls to functions before you have created them. first fix this issue and see if it resolves your problem.
window.onload=function addDelRefOrder()
{
var deliveryVal = document.getElementById("deliveryIdentification").value;
// Add to array
deliveryDummy.push(deliveryVal);
// Array changed, Re-Render
renderList(); /// <==== HERE
}
window.onload=function renderList() // <====== Function created here.
{
// clean the list:
while (list.firstChild) {
list.removeChild(list.firstChild);
}
// Recreate li
for(var i = 0; i < deliveryDummy.length; i++) {
var entry = document.createElement("li");
entry.appendChild(document.createTextNode(deliveryDummy[i]));
var removeButton = document.createElement('button');
removeButton.appendChild(document.createTextNode("Remove"));
removeButton.setAttribute('onClick','removeName('+i+')');
entry.appendChild(removeButton);
list.appendChild(entry);
}
}

Categories