Pre-selected option with ajax does not let me use the additional data


#1

So I have been trying to fix this for too long now driving me nutz.

I ahve ajax select option for customers… The data format I have returned in php sets up a html property for display. So the preselected option will show the additional data retreived via ajax in the drop down but it will not update the data for that selection.

So the selct2 on change will take the phone number and put it in anther feild. And this works for all options except the pre-selected option witch ever it may be at the time.

So in the console I can see the template result def has the data I need… (i need the phone number)

{id: "754", name: "Betty", email: null, phone: "118811881188", customer_note: null, …}

But the data from the selection change does not receive any of the information.
{
“selected”: true,
“disabled”: false,
“text”: “Betty”,
“id”: “754”,
“title”: “”,
“_resultId”: “select2-customer_id-wm-result-jk3k-754”,
“element”: {},
“wheelchair”: “0”
}

Why isnt the data being used. Its certainly being retrieved. Do I have to manually enter every piece of information for a pres-elected option? and how? Seems a bit silly to have to do any of that since it can already get all the data for the pres-elected option. Should only need to specify the ID of the selected option and have it fetch the rest.


#2

If you are following the documentation on how to pre-select an item retrieved via AJAX then you understand that you are hard-coding the pre-selected option into the underlying HTML select. The documentation is not clear on this point, but that hard-coded option is not related in any way to the items you actually retrieve via AJAX. It’s essentially a “hack” to create a displayable item when retrieving data via AJAX. This is because, when using AJAX to retrieve data for your Select2, no data (i.e., option element) is actually added to the underlying HTML select until the user makes a selection from the Select2 results list.

So you’re essentially correct that you’ll need to manually enter all of the extra data for the pre-selected option, because Select2 doesn’t associate that hard-coded option to the data items retrieved via AJAX, even if one of those elements has the same id value as the hard-coded option.

The advantage of using Select2’s built-in AJAX feature is that it only retrieves the data after the user enters a search term. This gives your data source the opportunity to filter a large data set and return only the items that match the user’s input. The disadvantage is as I’ve described it above.

If your data set is not too large, you could retrieve the data yourself (using php or JavaScript), store that data in a Select2 data array and use that array to initialize your Select2. If that data sets the selected attribute for the item you want to pre-select, then that item will appear selected when the Select2 is initialized, and it will be the actual complete data item. (It’s not clear, however, that the select event will be fired; you might need to manually call your function that handles the select event and pass it that selected item.)

Alternatively, if your data set is large, you could arrange for your data source to be able to send you just the item you want to pre-select, in addition to reacting to Select2’s AJAX queries. That would allow you to use php or JavaScript to retrieve the complete data for the pre-selected item, which you could uset to “hard-code” your pre-selected option in the HTML select, and (as above) manually call your select event handler with the pre-selected data if you need to process it immediately. Then (or before you call your event handler), you could initialize your Select2 with your AJAX data source and the pre-filled select element.


#3

Hey thanks for the reply. I am using the creatw option form the documentation for creating new items int he datbase and selecting them on the fly.

And now I have changed to using the same method for creating the pre-selected option as well including all the data but it still seems to drop information once its added.

The pre-selected option is as follows…

//              [               PRE FILL CUSTOMER AND PHONE             ]
    <?php if ($customer):?>
    //todo :  preselected option does not work if change to another option and back again it looses data
    BookingForm.Customer = '<?=json_encode($customer)?>';
    BookingForm.Customer = JSON.parse(BookingForm.Customer);
    BookingForm.Customer.text = BookingForm.Customer.name;
    console.log('pre-selected customer:', BookingForm.Customer);
    BookingForm.Customer = setSelect2Option(
        BookingForm.Select2customer.Dom,
        BookingForm.Customer.text,
        BookingForm.Customer.id,
        BookingForm.Customer);
    <?php endif;?>

//pre-selected data console log form above outputs:
{
  "id": "754",
  "name": "Betty",
  "email": null,
  "phone": "118811881188",
  "customer_note": null,
  "customer_wheelchair": "1",
  "created_at": "2018-09-14 10:00:47.000000",
  "text": "Betty"
}

This is the code for the setSelctOption funciton that has been working for creatinng NEW options that dont previosuly exist int he database.

function setSelect2Option(select2, text, id, data = {id: id, text: text}) {
if (debugging) console.log('setSelect2Option data received:', data);
//Note: For Select2 controls that receive their data from an AJAX source, using .val() will not work. You have to create an item manually.
//see https://select2.org/programmatic-control/add-select-clear-items#preselecting-options-in-an-remotely-sourced-ajax-select2
select2 = $(select2);
// create the option and append to Select2
var option = new Option(text, id, true, true);
select2.append(option).trigger('change');

// manually trigger the `select2:select` event
select2.trigger({
    type: 'select2:select',
    params: {
        data: data
    }
});
return select2.select2('data')[0];//just in case u want the new data as is set in select2

}

//console log from inside the setSelect2 Function
{
“id”: “754”,
“name”: “Betty”,
“email”: null,
“phone”: “118811881188”,
“customer_note”: null,
“customer_wheelchair”: “1”,
“created_at”: “2018-09-14 10:00:47.000000”,
“text”: “Betty”
}

//and this is the console log form the select2 change event ( selectElement.select2(‘data’)[0] )
{
“selected”: true,
“disabled”: false,
“text”: “Betty”,
“id”: “754”,
“title”: “”,
“_resultId”: “select2-customer_id-e1-result-5ehh-754”,
“element”: {},
“wheelchair”: “0”
}

So as even tho I am passing all the data in the data that ends up being in the select2 now had no phone number and has a title feild.

//here is my on change
BookingForm.Select2customer = {

        Dom: $('#booking_modal .customer.select2'),
        /**
         * Sets up Select2 with Ajax data
         * @returns {jQuery.fn.init|jQuery|HTMLElement|*}
         */
        init: function () {
            this.Dom.select2({
                placeholder: 'Select a Customer',
                tags: true,
                templateResult: function (item) {
                    //console.log('data from templateResult',item);
                    //note: searching and TAG option don't appear since they have no .html value (that comes form server results only)
                    //but we don't care coz we have a create customer option coming from php
                    return $(item.html); //return html sent via php as dom element for displaying html in the select
                },
                ajax: {
                    url: '<?=base_url()?>customers/select2',
                    dataType: 'json'
                }
            });
            
            return this.Dom; //so we can chain from init()
        },
    };
    
    
    //SELECT2 - Customer
    BookingForm.Select2customer.init().change(function () {
        let selectElement = $(this);
        let item = selectElement.select2('data')[0]; //select2 returns an array with a single object inside hence [0]...

        console.log('select2 selected item data',item);
        
        //create customer
        if (parseInt(item.id) < 0) {
            if (BookingForm.Customer && item.id === BookingForm.Customer.id){
                if (debugging) console.log('//oops triggered twice already created this guy');
            } else {
                $.ajax({
                    url: '<?php echo base_url()?>customers/insert',
                    type: 'POST',
                    data: {name: item.name}, //warning: you cant just hand over the item as select2 carries over extra prototype constructs that causes a stack overflow for some reason. Took me a hwile to realize this 😡.
                    success: function (response) {
                        if (debugging)console.log('create customer complete',response);
                        if (hasError(response)) {
                            flash(response, 'error');
                        }
                        else {
                            flash('New Customer was created.');
                            response = JSON.parse(response);//remember to convert your json strings to objects!!
                            if (debugging) console.log('New Customer was created: ',response);
                            BookingForm.Customer = setSelect2Option(selectElement, response.name, response.id, response);
                            BookingForm.Customer.isNew = true;
                        }
                    }
                });
            }
        }
        BookingForm.Customer = selectElement.select2('data')[0];//important: getting the item again as it may have changed with create customer

        if (debugging) {
            console.log('Selected Customer: ', BookingForm.Customer);
        }

        $("#booking_phone").val(BookingForm.Customer.phone ? BookingForm.Customer.phone :  '');

        $("#booking_wheelchair").prop('checked', (BookingForm.Customer.customer_wheelchair > 0))
            .trigger('change');//don't forget to trigger change events!
        
        //NTH: pre-fill the last used pickup location and drop off address as well????

        // END CUSTOMER CHANGE
    });

Can you see where I am going wrong? The data going in is correct but doen’t get added by select2.


#4

I’m not positive that the change event handler will receive (or have access to) the “complete” data object that you specified when you manually triggered the select2:select event. Your event handler should receive an Event object that contains a ‘params’ field whose value is the “params” object you used to trigger the `select2:select


#5

It’s not clear that the call to the Select2.data() method in your change event handler will actually return the data object you passed when you manually triggered the select2:select event. Can you try these two things and see if either of them works:

  1. In your change event handler, use the Event object passed to the handler—rather than calling the Select2.data() method—to retrieve the Event’s params.data object, and see whether that contains your full custom data object.
  2. Swap out the change event for the select2:select event in your event handler, and see if the Event object passed to that handler contains your data object in its params.data object.

If #1 works, you’re done. If #2 works, then you have to decide whether handling the select2:select event instead of the change event is sufficient for your needs. (It seems to me the only difference between the change event and the select2:select event is that the latter can be cancelled. So I would think this swap would be OK. But only you can really determine that.)


#6

Hey m8, thanks for suggestions sorry took me so long to come back to this issue as I had more pressing matters and took me a while to remember what was going on here again. But thanks to your comment I was able to jump back on with a clear head and direction :stuck_out_tongue_winking_eye:.

So… changing the event changed nothing however,
option #1 did show the full data in in the change event witch of course it should since that’s got the original data source witch was a bit of a DUH! moment after I changed the following line of code.

BookingForm.Customer = event.params.data;//important: use instead of select2 data so that pre-filled option works
        //BookingForm.Customer = selectElement.select2('data')[0];//important: getting the item again as it may have changed with create customer

All my Betty’s are giving out their phone numbers now :raised_hands::raised_hands::raised_hands::raised_hands:

And creating new Betty’s on the fly is still working.

Thanks for the help.


#7

Hey, Brad–

Aus-some, m8 :grinning:! I’m glad you got it working.

And thanks for the Like. I gave you one, too!

Good luck with the rest of your project.

–John