In my current application you can define some trainings for your sport club. Those trainings are saved into a Database and when you open the overview, you are able to see a list of all the trainings within the database.
Depending on the role of the user who is currently logged in(ADMIN or TRAINER) you can either edit/delete those trainings or not. Admin can, Trainer cannot. If you are not an admin, the buttons edit/delete are disabled.
The overview is rendered with this function:
function renderOverview() {
clearDynamic();
let template = document.getElementById("training-overview-TPL");
let tClone = template.content.cloneNode(true);
let wrapper = tClone.querySelector(".trainings-wrapper");
dynamic.append(wrapper);
let req = {
task: "fetch_all_trainings",
};
fetch(API_TRAININGS, {
headers: {"Content-Type":"application/json"},
method: "post",
body: JSON.stringify(req),
})
.then(res => res.json())
.then(async data => {
if(data.allTrainings.length > 0) {
data.allTrainings.forEach(async training => {
let row = await renderTrainingRow(training);
wrapper.append(row);
});
}
})
.then(() => {
fetch(API_LOGGED_USER)
.then(res => res.json())
.then(user => {
if(user.role !== "ADMIN") {
console.log("Logged User is NOT AN Admin");
let BTNS_edit = document.querySelectorAll(".BTN_edit_training");
let BTNS_delete = document.querySelectorAll(".BTN_delete_training");
console.log(BTNS_edit);
console.log(BTNS_delete);
setTimeout(() => {
BTNS_edit.forEach(e => {
e.disabled = true;
});
BTNS_delete.forEach(e => {
e.disabled = true;
});
}, 200);
}
});
});
}
wihtin that function there is another function “renderTrainingRow” invoked for the number of trainings that are fetched from the backend. renderTrainingRow creates a single Row that includes the edit/delete-buttons:
async function renderTrainingRow(training) {
let template = document.getElementById("training-row-TPL");
let tClone = template.content.cloneNode(true);
let name = tClone.querySelector(".name-cell");
name.innerText = `${training.trainingname}`;
let BTN_edit = tClone.querySelector(".BTN_edit_training");
BTN_edit.addEventListener("click", () => {
renderEditTrainingForm(training.trainingid);
});
let BTN_delete = tClone.querySelector(".BTN_delete_training");
BTN_delete.addEventListener("click", () => {
deleteTraining(training.trainingid);
});
// parse the fetched Group Array from JSON to JS Array:
let trainingGroupsAsIDsInArray = JSON.parse(training.traininggroups);
let req = {
task: "fetch_all_groups",
};
// get all the existing groups:
let groupResponse =
await fetch(API_GROUPS, {
headers: {"Content-Type":"application/json"},
method: "post",
body: JSON.stringify(req),
});
let res = await groupResponse.json();
let allGroups = res.allGroups;
// which one of all groups does the user belong to:
let groupsOfTraining = allGroups.filter(function(curr) {
return trainingGroupsAsIDsInArray.includes(curr.groupid);
});
let group = tClone.querySelector(".group-cell");
let groupcelltext = "<strong class='me-1'>Gruppen: </strong>";
if(groupsOfTraining.length > 0)
{
groupsOfTraining.forEach(grp => {
groupcelltext += ` <span class='badge bg-tertiary me-1'>${grp.groupname}</span> `;
})
}
else {
groupcelltext += " - ";
}
group.innerHTML = groupcelltext;
return tClone;
}
As you can see in the function renderOverview, within the last then() – Method of the then() – Chain that starts after the fetch of all Trainings, thats the part where the programm decides if the buttons are clickable or not(depending of the role of the fetched logged user)
It all works very good, but here is the problem: Sometimes the last trainings are not affected by the disabling of the buttons. As you can see in the following picture, the last entry is not affected. Sometimes its only the last entry, but sometimes the last 2 entries are affected. Most of the times every entry is affected but i dont want to make it dependant of luck. If i had to estimate, my gut tells me that 70% of the times every entry is affected, 15% the last one isnt and 15% the last 2 entries are not affected. But look for yourselfs how it looks:
Last Edit-Button is not disabled
Looking at the way it behaves i am pretty sure it has sth to do with the async nature of the program, but i cannot find out why, because i assumed that every then() methods waits, till the previous one has finished. I even added that setTimeout to “give javascript some time” getting every entry, but it also didnt work.
As you can see in the DOM i console.log all the affected buttons and the console only shows me the ones that got affected, not just every button that was rendered in the then()-Method before:
Not affected
Whats the nature of my problem and how do i solve it?