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
.