Explorar el Código

Select2 v4 compatibility for model autocomplete

Sullivan SENECHAL hace 8 años
padre
commit
dd4695ee61
Se han modificado 1 ficheros con 83 adiciones y 21 borrados
  1. 83 21
      Resources/views/Form/Type/sonata_type_model_autocomplete.html.twig

+ 83 - 21
Resources/views/Form/Type/sonata_type_model_autocomplete.html.twig

@@ -16,6 +16,13 @@ file that was distributed with this source code.
         {%- if required %} required="required"{% endif %}
     />
 
+    <select id="{{ id }}_autocomplete_input_v4" data-sonata-select2="false"
+        {%- if read_only is defined and read_only %} readonly="readonly"{% endif -%}
+        {%- if disabled %} disabled="disabled"{% endif -%}
+        {%- if required %} required="required"{% endif %}
+    >
+    </select>
+
     <div id="{{ id }}_hidden_inputs_wrap">
         {% if multiple -%}
             {%- for idx, val in value if idx~'' != '_labels' -%}
@@ -28,8 +35,15 @@ file that was distributed with this source code.
 
     <script>
         (function ($) {
-            var autocompleteInput = $('#{{ id }}_autocomplete_input');
-            autocompleteInput.select2({
+            // Select2 v3 does not used same input as v4.
+            // NEXT_MAJOR: Remove this BC layer while upgrading to v4.
+            var usedInputRef = window.Select2 ? '#{{ id }}_autocomplete_input' : '#{{ id }}_autocomplete_input_v4';
+            var unusedInputRef = window.Select2 ? '#{{ id }}_autocomplete_input_v4' : '#{{ id }}_autocomplete_input';
+
+            $(unusedInputRef).hide();
+            var autocompleteInput = $(usedInputRef);
+
+            var select2Options = {
                 {%- set allowClearPlaceholder = (not multiple and not required) ? ' ' : '' -%}
                 placeholder: '{{ placeholder ?: allowClearPlaceholder }}', // allowClear needs placeholder to work properly
                 allowClear: {{ required ? 'false' : 'true' }},
@@ -37,19 +51,27 @@ file that was distributed with this source code.
                 readonly: {{ read_only is defined and read_only or attr.readonly is defined and attr.readonly ? 'true' : 'false' }},
                 minimumInputLength: {{ minimum_input_length }},
                 multiple: {{ multiple ? 'true' : 'false' }},
-                width: '{{ width }}',
+                width: function() {
+                    // Select2 v3 and v4 BC. If window.Select2 is defined, then the v3 is installed.
+                    // NEXT_MAJOR: Remove Select2 v3 support.
+                    return Admin.get_select2_width(window.Select2 ? this.element : jQuery(this));
+                },
                 dropdownAutoWidth: {{ dropdown_auto_width ? 'true' : 'false' }},
                 containerCssClass: '{{ container_css_class ~ ' form-control' }}',
                 dropdownCssClass: '{{ dropdown_css_class }}',
-                initSelection : function (element, callback) {
-                    callback(element.val());
-                },
                 ajax: {
                     url:  '{{ url ?: path(route.name, route.parameters|default([]))|e('js') }}',
                     dataType: 'json',
                     quietMillis: {{ quiet_millis }},
                     cache: {{ cache ? 'true' : 'false' }},
                     data: function (term, page) { // page is the one-based page number tracked by Select2
+                        // Select2 v4 got a "params" unique argument
+                        // NEXT_MAJOR: Remove this BC layer.
+                        if (typeof page === 'undefined') {
+                            page = typeof term.page !== 'undefined' ? term.page : 1;
+                            term = term.term;
+                        }
+
                         {% block sonata_type_model_autocomplete_ajax_request_parameters %}
                         return {
                                 //search term
@@ -91,21 +113,59 @@ file that was distributed with this source code.
                         };
                         {% endblock %}
                     },
-                    results: function (data, page) {
-                        // notice we return the value of more so Select2 knows if more results can be loaded
-                        return {results: data.items, more: data.more};
-                    }
-                },
-                formatResult: function (item) {
-                    return {% block sonata_type_model_autocomplete_dropdown_item_format %}'<div class="{{ dropdown_item_css_class }}">'+item.label+'<\/div>'{% endblock %};// format of one dropdown item
-                },
-                formatSelection: function (item) {
-                    return {% block sonata_type_model_autocomplete_selection_format %}item.label{% endblock %};// format selected item '<b>'+item.label+'</b>';
                 },
                 escapeMarkup: function (m) { return m; } // we do not want to escape markup since we are displaying html in results
-            });
+            };
+
+            // Select2 v3/v4 special options.
+            // NEXT_MAJOR: Remove this BC layer while upgrading to v4.
+            var templateResult = function (item) {
+                return {% block sonata_type_model_autocomplete_dropdown_item_format %}'<div class="{{ dropdown_item_css_class }}">'+item.label+'<\/div>'{% endblock %};// format of one dropdown item
+            };
+            var templateSelection = function (item) {
+                // Select2 v4 BC select pre-selection.
+                if (typeof item.label === 'undefined') {
+                    item.label = item.text;
+                }
+                return {% block sonata_type_model_autocomplete_selection_format %}item.label{% endblock %};// format selected item '<b>'+item.label+'</b>';
+            };
 
-            autocompleteInput.on('change', function(e) {
+            if (window.Select2) {
+                select2Options.initSelection = function (element, callback) {
+                    callback(element.val());
+                };
+                select2Options.ajax.results = function (data, page) {
+                    // notice we return the value of more so Select2 knows if more results can be loaded
+                    return {results: data.items, more: data.more};
+                };
+                select2Options.formatResult = templateResult;
+                select2Options.formatSelection = templateSelection;
+            } else {
+                select2Options.ajax.processResults = function (data, params) {
+                    return {
+                        results: data.items,
+                        pagination: {
+                            more: data.more
+                        }
+                    };
+                };
+                select2Options.templateResult = templateResult;
+                select2Options.templateSelection = templateSelection;
+            }
+            // END Select2 v3/v4 special options
+
+            autocompleteInput.select2(select2Options);
+
+            // Events structure is different between v3 and v4
+            // NEXT_MAJOR: Remove BC layer.
+            var boundEvents = window.Select2 ? 'change' : 'select2:select select2:unselect';
+            autocompleteInput.on(boundEvents, function(e) {
+                if (e.type === 'select2:select') {
+                    e.added = e.params.data;
+                }
+                if (e.type === 'select2:unselect') {
+                    e.removed = e.params.data;
+                }
 
                 // console.log('change '+JSON.stringify({val:e.val, added:e.added, removed:e.removed}));
 
@@ -162,14 +222,16 @@ file that was distributed with this source code.
                 {%- if multiple -%} ] {%- endif -%};
             {% endif -%}
 
-            if (undefined==data.length || 0<data.length) { // Leave placeholder if no data set
+            // Select2 v3 data populate.
+            // NEXT_MAJOR: Remove while dropping v3 support.
+            if (window.Select2 && (undefined==data.length || 0<data.length)) { // Leave placeholder if no data set
                 autocompleteInput.select2('data', data);
             }
 
             // remove unneeded autocomplete text input before form submit
-            $('#{{ id }}_autocomplete_input').closest('form').submit(function()
+            $(usedInputRef).closest('form').submit(function()
             {
-                $('#{{ id }}_autocomplete_input').remove();
+                $(usedInputRef).remove();
                 return true;
             });
         })(jQuery);