Each click triggers different API call - javascript

I have list and each clicked item triggers different API request. Each request have different duration. On success I'm displaying some data.
Issue is that when I click on item#1 which takes approx 6000 to load, and just after on item#2 which takes 2000 to load, I will have the last clicked item displayed - which is item#2 because it has already loaded and once item#1 has received data my data will change to that. This is wrong as I want to display data from the latest click.
This is how I handle event:
newList.on('click', 'li', (e) => {
let id = $(e.currentTarget).data("id");
store.getCharacterDetails(id).then(docs => {
this.clearDetails();
this.charDetails = docs;
this.displayDetails(this.charDetails);
})
My API is a simulation from store object.
I suppose this works as expected but I do want the last triggered request to be valid.

A crude and simple method can be creating an array and pushing the IDs and after the asynchronous operations you can just check if it is the latest click or not. But pitfall is that if clear and displayDetails takes much time and if someone click while it was clearing and displaying it will not register the latest click.
Anyway, here is the code maybe you can make something better out of it.
var latestClick = [];
newList.on('click', 'li', (e) => {
let id = $(e.currentTarget).data("id");
latestClick.push(id);
store.getCharacterDetails(id).then(docs => {
if(id === latestClick[latestClick.length - 1]){
this.clearDetails();
this.charDetails = docs;
this.displayDetails(this.charDetails);
latestClick = [];
}
})
})

Make charDetails an object that keeps all of the results, keyed by the ids. Keep track of the last clicked id.
// in constructor
this.charDetails = {};
this.lastId = null;
newList.on('click', 'li', (e) => {
let id = $(e.currentTarget).data("id");
this.lastId = id;
if (this.charDetails[id] === id) { // don't cancel requests, cache them!
this.displayDetails(this.charDetails[id])
} else {
store.getCharacterDetails(id).then(docs => {
// this runs later, cache the result
this.charDetails[id] = docs;
if (id === lastId) { // only update UI if the id was last clicked
this.displayDetails(docs)
}
});
}
});

Related

Optimize Javascript code to find records in array that match criteria

I have a parent component which is a list of records and in one of the child component I have a form that submits data and if successful is added to that list in the parent. Everytime the data is submitted, I need to check if there is an identical record with the same title. This child form component is used to add and edit records, so if the record is edited then I also have to check that it can be submitted with the same name of ofcourse. Below is the code I have and it is working fine but I have been thinking if there is a better way of writing this. Can it be done while going thru the list array the first time instead of going through it once and then going thru it again to check for unique items.
When the data is submitted in the child component (form) I am executing the following functions to see if title field is unique.
const isUniqueTitle = (title) => {
if(activities.find(activity => activity.title.toLowerCase() === title)){
// shows alert
return false;
}
return true;
}
// Child component/form calls this function with the form data
const validateData = data = {
let isUnique = true;
//activities below is available here in the parent
activities.forEach(activity => {
// check for id below tells me that its a record being edited so only do a check if the title
// has been changed else if there is no id then it means its a new record so continue with the
// check
if (activity.id && activity.title != activity.title) {
isUnique = isUniqueTitle(data.title);
} else if (!activity.id) {
isUnique = isUniqueTitle(data.title);
}
return isUnique;
})
}
Please advise, thank you!
You could use a Set to store titles and use its has method to check for the uniqueness of any given title
The Set object lets you store unique values of any type
const titleSet = new Set(activities.map(activity => activity.title.toLowerCase()))
const isUniqueTitle = (title) => {
return titleSet.has(title);
}
// Child component/form calls this function with the form data
const validateData = data = {
//activities below is available here in the parent
activities.forEach(activity => {
// check for id below tells me that its a record being edited so only do a check if the title
// has been changed else if there is no id then it means its a new record so continue with the
// check
if (activity.id && activity.title != activity.title) {
isUniqueTitle(data.title);
} else if (!activity.id) {
isUniqueTitle(data.title);
}
})
}

Tried my best to stop duplicating data on continuous http request by clicking refresh button(for that particular component) Angular 5

The below is my .ts file for the Alarm Component and over HTML I am using a simple *ngFor over criticalObject.siteList to display the records
This is not the original code I have simplified this but the problem I am facing is that on rigorous click on the refresh button(fires HTTP request), the list is adding duplicate siteNames and that should not happen. I have heard of debounce time, shareReplay, and trying applying here, which even doesn't make sense here.
NOTE: I have to fire the HTTP request on every refresh button click.
Keenly Waiting for Help.
criticalObject.siteList = [];
siteList = ["c404", "c432"];
onRefresh() {
this.criticalObject.siteList = [];
this.siteList.forEach(elem => {
getAlarmStatus(elem);
})
}
getAlarmStatus(item) {
critical_list = [];
alarmService.getAlarmStatusBySite(item.siteName).subcribe(data => {
if(data) {
// do some calculations
if(this.criticalObject.siteList.length === 0) {
this.criticalObject.siteList.push({
siteName = item.siteName;
})
}
this.criticalObject.siteList.forEach((elem, idx) => {
if(elem.siteName === item.siteName) {
return;
} else if(idx === this.criticalObject.siteList.length - 1) {
this.criticalObject.siteList.push({
siteName = item.siteName;
})
}
})
}
}
})
I did a silly mistake, I am new to JavaScript, I found out you cannot return from a forEach loop and that's why I was getting duplicated records, return statement in forEach acts like a continue in JavaScript.

LocalStorage data appearing in console but not in the DOM

I am currently building a to-do list app and I have already saved the data using the localStorage API.
The desired data, however, appears in the console whenever I console.log it but it still doesn't save in the DOM.
I also added the getItem() function and logged it into the console and I can still view it in the console, find here:
getItem content in the console
But it just doesn't store in the browser
Seeing this, it is in your inclination that it should have stored in the DOM and the content still remains after reloading but that just isn't the case here.
This function below adds a new item to the list, it also deletes and crosses out completed items too:
let id = 0;
function addTaskFunc() {
const aTask = `
<div class="task" id="task-${id}">
<button class="done__btn">
<i class="far fa-check-square"></i>
</button>
<p>${box.value}</p>
<button class="priority">Make priority</button>
<button class="cancel__btn">
<i class="far fa-times-circle"></i>
</button>
</div>
`;
const x = box.value;
if (x) {
taskList.insertAdjacentHTML('afterbegin', aTask);
box.value = '';
;
let cid = id;
const cancelBtn = document.querySelector(`#task-${id} .cancel__btn`);
cancelBtn.addEventListener('click', () => {
deleteItem(cid);
});
let cid2 = id;
// code for marking out an item as done
let cid3 = id;
// code for appending an item to the top of DOM
let cid4 = id;
persistData(cid4);
readData(cid4);
id++;
}
}
newTask.addEventListener('click', addTaskFunc); // Button to activate the function
persistData = id => {
const el = document.querySelector(`#task-${id}`);
localStorage.setItem('addedTasks222', el.innerHTML);
};
readData = id => {
const el = document.querySelector(`#task-${id}`);
const saved = localStorage.getItem('addedTasks222');
if (saved) el.innerHTML = saved;
console.log(el.innerHTML); // This line of code appears in the console
}
I also tried doing it this way inside of the addTaskFunc:
const r = document.querySelector(`#task-${id} .task`);
r.addEventListener('load', () => {
persistData(cid4);
readData(cid4);
});
When I try it with the method above I get the error code in the console:
Cannot read property of addEventListener of null.
I feel there is something wrong somewhere but I just cannot seem to find out where I am missing it.
One last thing, the localStorage only seems to store only one item into the key. Do I need a loop to sort that out?
you can store an array in the local storage like this
localStorage.setItem('array', JSON.stringify(YOURARRAY))
and then you can load that with
var restoredArray = JSON.parse(localStorage.getItem('array'));
sorry for the time..
So I was looking in to your code, and appears that you have some problems..
1- The first one I saw, was in the persistData() function. Accidentally you are just selecting one task to store with document.querySelector(.task);
to select all you need to use document.querySelectorAll(.task), this will return you an array. Because of that you only store one task.
2- Secondly is that you are trying to store html. with .innerHtml, and the innerHtml of your (".class") is buttons and etc.. You should store values.
3- At the end, when you are trying to print the data, you do document.querySelector(.task), and as you can see its returning you undefined, that's because you haven't a .task div yet.
So How you can appropriately make your app work.
1- The first thing you need to do is creating an array variable up on your js file.
let tasks = [];
2-That array will be used to store the values of your tasks with something like this
function addItem(){
let value = item.value; //you need to define the item some way
tasks.push(value);
print() //a function to print the values;
persistData() // store the array
}
3-To print the tasks
function print(){
tasks.forEach(task => {
let div = document.createElement("div");
div.innerHTML = `copy your html task to here, use ${task} to print the task value`
})
}
4-Store the tasks
function persistData(){
localStorage.setItem('addedTasks', tasks); //store the array
};
function readData(){
if (typeof(Storage) !== "undefined") { //check if the browser has localStorage
tasks = localStorage.getItem('addedTasks'); //update your array
print();
} else {
//No Web Storage message
}
};
5- Just run readData() when you load your document with
document.addEventListener("DOMContentLoaded", function(event) {
readData();
});
I hope that I can help

How to get value and change outer variable inside event handlers?

I have empty array. Need to fill it by clicking some links (only if value of current index is not filled already).
$(document).ready(function () {
var genres_items = [];
$('.genre-fill-link').on('click', function() {
var genre_index = $(this).data('g-index'); // get id of genre
if(!genres_items[genre_index] { // want to check is this genre filled - get error, undefined variable
$.get('/get-genre-list/', {'genre-id', gener_index}, function(data) { // if genre is not filled yet, want to get data by ajax
gener_items[gener_index] = data;
});
}
}
console.log(genres_items); // get empty untouched array, even if links clicked
});
How to fill all elements of array genre_items (every element once by clicking .genre-link) ?
How to get values of this array in others handlers and callbacks afterwards?!
Javascript confused me =\ Please help
check my comments and try this :
$(document).ready(function () {
var genres_items = [];
$('.genre-fill-link').on('click', function() {
var genre_index = $(this).data('g-index'); // get id of genre
if(!genres_items[genre_index] { // want to check is this genre filled - get error, undefined variable
$.get('/get-genre-list/', {'genre-id', gener_index}, function(data){ // if genre is not filled yet, want to get data by ajax
genres_items[genre_index] = data;
console.log(genres_items);
});
}
}
});

Make code wait for popup to open, then scrape the popup

I am writing a JS script that automates some browser actions.
With get_baskets_onclicks, I am collecting onclick functions from certain DOM elements and returning them in an array. Each onclick looks something like this:
onclick="PrimeFaces.ab({s:"j_id_32:GenerationTable:0:j_id_1e_2_3p",u:"#widgetVar(GenerationCodingDialog)",onco:function(xhr,status,args){PF('GenerationCodingDialog').show();}});return false;"
and opens a pop-up from which I need to collect some data with get_MAP_data.
Also, each of these functions is called from within get_MAP_data.
The problem is I cannot make the code wait for the popup to be opened, so the data returned by get_MAP_data is empty.
Besides the below document.readyState === 'complete', I have also tried window.onload = function(){}, to no avail.
Is there any way to make the browser (Chrome) wait? I guess I cannot use jQuery, because this is not my webpage.
function get_baskets_onclicks() {
// returns array of functions that launch MAP dialogs
var baskets = Array.from(document.getElementsByClassName("ui-commandlink ui-widget margin-right-5px"));
var baskets_onclicks = baskets.map(basket => basket.onclick);
return baskets_onclicks;
};
function get_MAP_data(basket_onclick) {
basket_onclick()
if (document.readyState === 'complete') {
console.log("PAGE LOADED");
// wait here for the dialog to open
// dt = detail table
var MAP_data = {} // container for transaction details
var labels_to_get = ['Description', 'Category', 'Department', 'Justification', 'My Shop Voucher', 'My Shop Coding'];
var all_dts = document.getElementsByClassName('summary-details-grid');
var dt = Array.from(all_dts).filter(table => table.parentElement.id == "paymentGenerationmyShopCodingForm")[0];
var dt_body = dt.children[0];
var dt_trs = Array.from(dt_body.children) ;
dt_trs.forEach(function(tr) {
tds = Array.from(tr.children);
tds.forEach(function(td) {
var label = td.textContent;
if (labels_to_get.includes(label)) {
var value_for_label = tds[1].textContent;
MAP_data[label] = value_for_label;
console.log(label, value_for_label);
};
});
});
// console.log(MAP_data);
return MAP_data;
};
};
var first_onclick = get_baskets_onclicks()[0];
get_MAP_data(first_onclick);
A small, hacky fix would be to make your code poll for the existence of the elements you are checking.
let interval = setInterval(() => {
var all_dts = document.getElementsByClassName('summary-details-grid');
if (all_dts.length !== 0) {
// Found some elements, now lets run the code
clearInterval(interval);
get_MAP_data(first_onclick);
}
}, 100);
This would check for summary-details-grid classes ever 10th of a second, and when it finds them, run your code.
https://developer.mozilla.org/en-US/docs/Talk:DOM/window.setTimeout
http://mdn.beonex.com/en/DOM/window.setInterval.html
combination of retries, setTimeout, setInterval, while loop, I'm not sure the best way for your specific modal, but one of those options should be fit for polling the DOM results until something is there or it's tried to many times.

Categories