Setting initial values on load with Select2 ajax AND templating

How do I set the first value shown in the select2 (before the user searches) and so that I can configure the templating extra fields (state and city)?

    $('.postcode2').select2({
        ajax: {
            url: function (params) {
                return "{{ url('/') }}" + "/api/postcodes/" + params.term;
            },
            processResults: function (data) {
                return {
                    results: data,
                };
            },
            cache: true,
            dataType: 'json',
            delay: 100,
        },
        minimumInputLength: 2,
        escapeMarkup: function(result) {
            return result;
        },
        templateSelection: function(result) {
            return result['text'] + ' (' + result['city'] + ', ' + result['state'] + ')';
        },
        templateResult: function (result) {
            if (result.loading) return 'Searching...';
            return '<strong>' + result['text'] + '</strong>' + 
                   '<h6>' + result['city'] + ', ' + result['state'] + '</h6>';
        },
    });

Did you try the suggested approach in the Select2 documentation?

Yes I tried that but I could not make it work with templates? (I would rather not do it through a additional API call)

The below hack does what I need it to do, but it is terribly inefficient…

        templateSelection: function(result) {
            if (result['city'] == null) {
                result['city'] = "INITIAL_CITY";
                result['state'] = "INITIAL_STATE";
                result['country'] = "INITIAL_COUNTRY";
            };
            return result['text'] + ' (' + result['city'] + ', ' + result['state'] + ', ' + result['country'] +')';
        },

I’ve never tried programmatically inserting a selected option into an AJAX-backed Select2 that also uses a templateSelection, but I have done so with a Select2 that is backed by a JavaScript object, and it worked just fine (i.e., the template was applied to the programmatically injected selection). So I don’t see why it wouldn’t work in the AJAX-backed case. Do you have a code sample online (e.g., on codepen.io or jsfiddle.net) that I could look at?

FWIW, your “hack” solution doesn’t seem inefficient to me. The templateSelection callback is only called once per user selection. It doesn’t have to loop through all the data in the Select2, and your code is only processing a single if test that is not at all complicated. Yes, it’s a hack, but I doubt it consumes any significant amount of time or computing resources to perform.

Could you please share and example of your solution backed by a JavaScript object?

I’ve thought about this some more, and now I realize why the “standard” approach doesn’t work for you. It’s because you’re using the “extra” data that is supplied by your AJAX data source in your templateSelection callback, but that “extra” data is not present when you simply insert an HTML <option> element into the associated <select>. I don’t know of any way to associate “extra” data with a plain old <option> element in a way that Select2 would recognize it.

I’ve inspected the <option> and <select> HTMLElement objects that are associated with a populated Select2 widget, and I can’t find any property where the “extra” data provided by the AJAX (or JavaScript object) data source is stored in those objects; therefore, I believe that data is stored in the browser’s memory in a way that is not directly accessible to adopting applications.

So, I can think of one other possible way you might make this work, and that is to supply a JavaScript array containing an object with the initial data you want to display as the data property of the configuration object you supply to the Select2() initialization function. I don’t know whether this will work since you’re also specifying an AJAX data source, but it’s worth a try.

If that doesn’t work, then I think your “hack” is probably the best you can do, unless you want to supply your own SelectionAdapter implementation, in which case you could probably provide support for what you’re trying to do.

Not sure how to use a selectionadapter ???

Neither am I (other than the default implementation that’s supplied with Select2). The topic of Select2 adapters is not well documented, although if you Google “select2 adapter example”, there’s at least one tutorial on the web that seems pretty good.

Before you go to that trouble, however, did you try my other suggestion (using a JavaScript array to set the initial selection)?

Here is the workaround I’m using for the same issue. I’m using the sample code from the documentation as a starting point but, before I append the new option, I set my extra fields as custom data attributes.

// Fetch the preselected item, and add to the control
var studentSelect = $('#mySelect2');
$.ajax({
    type: 'GET',
    url: '/api/students/s/' + studentId
}).then(function (data) {
    // create the option and append to Select2
    var option = new Option(data.full_name, data.id, true, true);

    // SET CUSTOM DATA
    option.dataset.myCustomValue = 'Something';
    option.dataset.myOtherCustomValue = 'Something Else';
    // END SET CUSTOM DATA

    studentSelect.append(option).trigger('change');

    // manually trigger the `select2:select` event
    studentSelect.trigger({
        type: 'select2:select',
        params: {
            data: data
        }
    });
});

Then in my template functions, if my extra data doesn’t exist, I pull it from the element’s custom data attributes.

function myTemplateFunction(data) {
    if (! data.id) {
        return data.text;
    }

    var myCustomValue = data.myCustomValue || data.element.dataset.myCustomValue;
    var myOtherCustomValue = data.myOtherCustomValue || data.element.dataset.myOtherCustomValue;

    // More...
}