Search code examples
javascriptasp.net-corejquery-select2razor-pages

.onChange for select2 triggers twice


I'm learning how to implement cascading inputs using Select2 in .NET Core 6 Razor Pages. Unfortunately, I can't proceed due to code that's triggering twice.

Full code:

@section Scripts {

    <script src="~/js/jquery-3.7.0.js"></script>
    <script src="~/js/jquery.dataTables.min.js"></script>
    <script src="~/js/dataTables.bootstrap5.min.js"></script>

    <script src="~/js/select2.min.js"></script>
    <script src="~/js/picker.js"></script>
    <script src="~/js/picker.date.js"></script>

    <script src="~/lib/tinymce/tinymce.min.js"></script>




    <script type="text/javascript">

        var selectI = $('#selectTableInfo1');

        var selectII = $('#selectTableInfo2');

        $(document).ready(function () {

            selectI.select2({
                dropdownParent: $('#demoModal'),
                minimumInputLength: 3,
                placeholder: 'Select a range:'
            });

            selectII.select2({
                dropdownParent: $('#demoModal'),
                minimumInputLength: 3,
                placeholder: 'Select a range:',
                data: [],  // Empty initial data
                disabled: true
            });

            $('.button-container button').on('click', function (event) {
            event.preventDefault();
            var buttonId = $(this).attr('id');

            fetchDataForButton(buttonId);
        });
    });

The affected line in question is the selectI.on('change') line in the following code:

function fetchDataForButton(buttonId) {
    $.ajax({
        type: 'POST',
        headers: { RequestVerificationToken: $('input:hidden[name="__RequestVerificationToken"]').val() },
        url: '/wagetable?handler=CollectData',
        dataSrc: '',
        data: { buttonId: buttonId },

        success: function (response) {

            console.log(buttonId);

            populateSelectI(response);

            selectI.on('change', function () {
            
                var selectedValue = selectI.val();
                console.log(buttonId);
                // Check if selectI has a selected value
                if (selectedValue !== "") {
                    // Enable selectII
                    selectII.prop('disabled', false);
                    //console.log(buttonId);
                    // Populate selectII based on selectI's value
                    populateSelectII(buttonId, response, selectedValue);
                } else {
                    // If no value is selected, disable selectII and clear it
                    selectII.prop('disabled', true);
                    selectII.empty();
                }
            });

        },
        error: function (error) {
            console.error("Error fetching data: ", error);
            // Handle error as needed
        }
    });
}

The idea is once a button is pressed, the selectI input is populated with data; next, the user selects an option from said input. Once that happens, the selectII input is enabled and is also populated with data. Once selectII has value, the values from the 2 inputs are used for data population and append the results in a text area.

Problem

The problem is that the appended results are displayed twice; some information outright wrong. I didn't display it here because I narrowed down the fault at this line: selectI.on('change', function () {. 'change' somehow gets triggered twice, so does 'input' as well. 'click' doesn't do anything.

Solutions tried

I've tried changing the line with varying results:

  • selectI.off('change', function () { just makes the function not work
  • selectI.off('change').on('change', function () { makes the select input not display any selected value
  • selectI.on('change').off('change', function () { displays the selected value but doesn't enable the selectII input
  • selectI.on('change', function () { enables selectII but runs the code twice; meaning I get two sets of data in one run

The last one is not bad on its own, but because populateSelectII immediately calls another function that populates a textarea, the displayed information gets screwed. I'm testing the program in Google Chrome. Any advice on how to resolve this is greatly appreciated.


Solution

  • I'm learning how to implement cascading inputs using Select2 in .NET Core 6 Razor Pages. Unfortunately, I can't proceed due to code that's triggering twice.

    Actually based on your scenario and code snippet, it seems that you have multiple event binders within the fetchDataForButton function's success callback. Which causing redundant triggerswhich @CBroe already been mensioned in the comment.

    In addition to this, binding events inside the Ajax success callback might capture additional triggers during data processing or updates.

    In order to resolve your issue, you can do as following:

    Try moving the selectI.on('change') function outside the Ajax success callback, possibly to the $(document).ready function. This ensures the event listener is attached only once.

    Let's have a look in practice, how you could modify the exsisting code:

    $(document).ready(function () {
    
       
        selectI.on('change', function () {
            var selectedValue = selectI.val();
            console.log(buttonId);
            
            if (selectedValue !== "") {
                selectII.prop('disabled', false);
                populateSelectII(buttonId, response, selectedValue);
            } else {
                selectII.prop('disabled', true);
                selectII.empty();
            }
        });
    
        $('.button-container button').on('click', function (event) {
            event.preventDefault();
            var buttonId = $(this).attr('id');
    
            fetchDataForButton(buttonId);
        });
    });
    

    Note: By moving the selectI.on('change', function () {...} outside of the success callback, we are ensuring that the event is bound only once when the document is ready, preventing multiple triggering of the change event.