I'm working on a time submission application for contractors that work on special projects. I have two select elements, projectSelect and payPeriodSelect.
I use JavaScript promise chains to retrieve and load projects and pay periods. The pay period select is populated with options once a project is selected in the project select.
After the user clicks Submit, I save the time entries to the database and update the select option highlight colors, based on the timesheet status (green for approved, red for rejected, gray for submitted). I need to refresh the project and pay period select option lists so that the options are highlighted with the proper color.
In order to do this, I need to call getProjects() and getPayPeriods() again, and programmatically change the value of projectSelect and payPeriodSelect. I am able to change the value of payPeriodSelect with jQuery's .val() function. However, projectSelect value is set to null when I used the same method. Why does it work for one select but not the other?
The select elements are defined like this:
<select id='projectSelect' class="form-select"></select>
<select id='payPeriodSelect' class="form-select"></select>
The promise chain to load projects gets called when the page first loads:
getProjects()
.then(data => {
return loadProjects(data);
})
.then(data => {
$('#projectSelect').change(); // select the default project to display (1st option in list)
})
.catch(error => {
// display error in modal
});
function loadProjects(projectData) {
return new Promise((resolve, reject) => {
let select = document.getElementById("projectSelect");
select.length = 0;
projectData.reduce((previousPromise, project) => { // map each project object in the array to a new promise
return previousPromise
.then(x => getPayPeriods(project.id))
.then(payPeriods => {
let option = document.createElement("option")
if (payPeriods.length) {
// if all timesheets for the project have been approved, change highlight color of option to green
if (payPeriods.every(payPeriod => payPeriod.approvalStatus)) {
option.setAttribute('class', 'approvedColor')
}
// If all timesheets are rejected, change color to red
else if (payPeriods.every(payPeriod => payPeriod.rejectionStatus)) {
option.setAttribute('class', 'rejectedColor')
}
// if all timesheets are submitted, change color to gray
else if (payPeriods.every(payPeriod => payPeriod.submissionStatus)) {
option.setAttribute('class', 'submittedColor')
}
}
option.text = project.code + ' - ' + project.name
option.value = project.id
select.appendChild(option)
select.value = select.options[0].value // set 1st option's project ID as default value
return resolve(true)
})
.catch(error => {
return reject(error)
})
}, Promise.resolve()) // Promise.resolve() is the initial promise function that starts the chain
return resolve(true);
})
}
The change event for the projectSelect and the load pay period functions is shown below:
$('#projectSelect').on('change', function (e) {
$('tr').find("input.timeBox").val("") // clear timesheet inputs
let projectId = this.value
getPayPeriods(projectId)
.then(data => {
return loadPayPeriods(data)
})
.then(x => {
$('#payPeriodSelect').trigger('change')
})
.catch(error => {
})
})
function loadPayPeriods(data) {
return new Promise((resolve, reject)=>{
var select = document.getElementById("payPeriodSelect")
select.length = 0
if (!data.length) {
$('#payPeriodSelect').attr('disabled', true)
return reject(false)
}
// enable dropdown if there are pay periods to load into it
$('#payPeriodSelect').attr('disabled', false)
for (let payPeriod of data) {
let option = document.createElement("option")
option.text = payPeriod.start_d + ' - ' + payPeriod.end_d
option.value = payPeriod.start_d + '|' + payPeriod.end_d
// change pay period option highlight colors based on timesheet status
if (payPeriod.approval_d) {
option.setAttribute('class', 'approved')
} else if (payPeriod.rejection_d) {
option.setAttribute('class', 'rejected')
} else if (payPeriod.submission_d) {
option.setAttribute('class', 'submitted')
}
select.appendChild(option)
}
select.value = select.options[0].value
return resolve(true)
})
}
The payPeriodSelect change event:
$('#payPeriodSelect').on('change', function (e) {
// get and populate timesheet data for the selected pay period
getTimeData(this.value)
.then(data=> {
return loadTimeData(data)
})
.then(data => {
if (data) {
let payPeriodSelect = document.getElementById('payPeriodSelect')
if (data.approvalStatus) {
payPeriodSelect.options[payPeriodSelect.selectedIndex].setAttribute('class', 'approvedColor')
} else if (data.rejectionStatus) {
payPeriodSelect.options[payPeriodSelect.selectedIndex].setAttribute('class', 'rejectedColor')
} else if (data.submissionStatus) {
payPeriodSelect.options[payPeriodSelect.selectedIndex].setAttribute('class', 'submittedColor')
}
}
})
.then(x => {
return calculateTotalTime()
})
.catch(error => {
})
})
The function for submitting time. I get and load projects, and change the values for both select elements. projectSelect value is set to null while payPeriodSelect value is set to the correct value.
$('#submitTol').on('click', function (e) {
return saveDataToDatabase()
.then(data => {
return getProjects()
})
.then(data => {
return loadProjects(data)
})
.then(x => {
return new Promise((resolve, reject) => {
$('#projectSelect').val(project.id).change()
return resolve(true)
})
})
.then(x => {
return new Promise((resolve, reject) => {
$('#payPeriodSelect').val( payPeriod.start_d + '-' + payPeriod.end_d).change()
return resolve(true)
})
}
}
Sorry for the heap of code, but I wanted to give some context about how I retrieve and display data in the select elements. I am so confused as to why changing payPeriodSelect's value works, but not projectSelect.
<>inside of it, next to the "image" button), which lets you include jQuery as one of the many presupplied libraries. Ultimately, your specific code shouldn't really matter if you can reduce it to a clear example that will go wrong for everyone who tries to run that, and then your question will be valuable to other future users, too =)let select = document.getElementById("projectSelect");. Could this be why the pay period selector has no value?