0

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.

2
  • 4
    Please edit your post and turn this into a minimal reproducible example that uses a Runnable Snippet instead (the little document with <> 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 =) Commented Mar 17, 2024 at 23:17
  • It looks like the code is adding pay period options to the project selector - There doesn't appear to be any change in the code to let select = document.getElementById("projectSelect");. Could this be why the pay period selector has no value? Commented Mar 18, 2024 at 1:20

1 Answer 1

0

The method .change() doesn't change a <select> selected <option>, rather it's a short hand for .on('change') and .trigger('change'). Also it was deprecated in jQuery 3.3, so probably don't use it.

There is a difference between select.value and option.value. You most likely want the selected options value.

To get the value, you could use:

$('select option:selected').val(); //jquery

document.querySelector('select').selectedOptions[0].value // javascript

To set the value, you have to loop over all of the options and find the one that matches the property / text / value you want:

$('select option').each(function() { //jquery
  if (this.value === value) { this.selected = true; }
});

document.querySelectorAll('select option').forEach(function(option) { //javascript
  if (option.value === value) { option.selected = true; }
});
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.