Select2 4.0.0 valor inicial con Ajax

97

Tengo un select2 v4.0.0 poblado de una matriz Ajax. Si configuro el valor del select2, puedo ver a través de la depuración de JavaScript que ha seleccionado el elemento correcto (n. ° 3 en mi caso), sin embargo, esto no se muestra en el cuadro de selección, todavía muestra el marcador de posición. ingrese la descripción de la imagen aquí

Considerando que debería estar viendo algo como esto: ingrese la descripción de la imagen aquí

En mis campos de formulario:

<input name="creditor_id" type="hidden" value="3">
<div class="form-group minimal form-gap-after">
    <span class="col-xs-3 control-label minimal">
        <label for="Creditor:">Creditor:</label>
    </span>
    <div class="col-xs-9">
        <div class="input-group col-xs-8 pull-left select2-bootstrap-prepend">
            <select class="creditor_select2 input-xlarge form-control minimal select2 col-xs-8">
                <option></option>
            </select>
        </div>
    </div>
</div>

Mi javascript:

var initial_creditor_id = "3";
$(".creditor_select2").select2({
    ajax: {
       url: "/admin/api/transactions/creditorlist",
       dataType: 'json',
       delay: 250,
       data: function (params) {
           return {
                q: params.term,
                c_id: initial_creditor_id,
                page: params.page
           };
       },
       processResults: function (data, page) {
            return {
                results: data
            };
       },
       cache: true
   },
   placeholder: "Search for a Creditor",
   width: "element",
   theme: "bootstrap",
   allowClear: true
   }).on("select2:select", function (e) {
      var selected = e.params.data;
      if (typeof selected !== "undefined") {
           $("[name='creditor_id']").val(selected.creditor_id);
           $("#allocationsDiv").hide();
           $("[name='amount_cash']").val("");
           $("[name='amount_cheque']").val("");
           $("[name='amount_direct']").val("");
           $("[name='amount_creditcard']").val("");
        }
    }).on("select2:unselecting", function (e) {
        $("form").each(function () {
            this.reset()
        });
        ("#allocationsDiv").hide();
        $("[name='creditor_id']").val("");
    }).val(initial_creditor_id);

¿Cómo puedo hacer que el cuadro de selección muestre el elemento seleccionado en lugar del marcador de posición? ¿Debería enviar esto como parte de la respuesta AJAX JSON quizás?

En el pasado, Select2 requería una opción llamada initSelection que se definía cada vez que se usaba una fuente de datos personalizada, lo que permitía determinar la selección inicial del componente. Esto funcionó bien para mí en v3.5.

Greg
fuente
¿Como es posible? Cuando intento vincularme selectcon ajaxun error de subida, esa opción ajax no está permitida con seleccionar ...
Daggeto
Hola, por favor eche un vistazo a stackoverflow.com/questions/42833778/…
Vivekraj KR

Respuestas:

109

Está haciendo la mayoría de las cosas correctamente, parece que el único problema que tiene es que no está activando el changemétodo después de haber establecido el nuevo valor. Sin un changeevento, Select2 no puede saber que el valor subyacente ha cambiado, por lo que solo mostrará el marcador de posición. Cambiando tu última parte a

.val(initial_creditor_id).trigger('change');

Debería solucionar su problema y debería ver la actualización de la interfaz de usuario de inmediato.


Esto es suponiendo que usted tiene una <option>ya que tiene una valuede initial_creditor_id. Si no selecciona2, y el navegador, no podrá cambiar el valor, ya que no hay ninguna opción a la que cambiar, y Select2 no detectará el nuevo valor. Noté que tu <select>solo contiene una única opción, la del marcador de posición, lo que significa que tendrás que crear la nueva <option>manualmente.

var $option = $("<option selected></option>").val(initial_creditor_id).text("Whatever Select2 should display");

Y luego añádalo al en el <select>que inicializó Select2. Es posible que deba obtener el texto de una fuente externa, que es donde initSelectionsolía entrar en juego, lo que aún es posible con Select2 4.0.0. Al igual que una selección estándar, esto significa que tendrá que realizar la solicitud AJAX para recuperar el valor y luego configurar el <option>texto sobre la marcha para ajustarlo.

var $select = $('.creditor_select2');

$select.select2(/* ... */); // initialize Select2 and any events

var $option = $('<option selected>Loading...</option>').val(initial_creditor_id);

$select.append($option).trigger('change'); // append the option and update Select2

$.ajax({ // make the request for the selected data object
  type: 'GET',
  url: '/api/for/single/creditor/' + initial_creditor_id,
  dataType: 'json'
}).then(function (data) {
  // Here we should have the data object
  $option.text(data.text).val(data.id); // update the text that is displayed (and maybe even the value)
  $option.removeData(); // remove any caching data that might be associated
  $select.trigger('change'); // notify JavaScript components of possible changes
});

Si bien esto puede parecer mucho código, así es exactamente como lo haría para los cuadros de selección que no son de Select2 para asegurarse de que se hayan realizado todos los cambios.

Kevin Brown
fuente
14
¿Alguna idea de cómo preseleccionar múltiples opciones para cuadros de selección múltiple?
user396404
6
@ user396404 deben aplicarse los mismos conceptos, simplemente pase una lista de identificadores en val()lugar de un solo identificador. O simplemente agregue varios <option selected>y eso debería manejarse automáticamente.
Kevin Brown
5
Me doy cuenta de que esta respuesta tiene más de un año, pero la documentación de Select2 para la pregunta de preguntas frecuentes: "¿Cómo puedo configurar las opciones seleccionadas inicialmente cuando uso AJAX?" todavía apunta a esto. Adjunté un JSFiddle como ejemplo de trabajo para v4.0.3 jsfiddle.net/57co6c95
Clint
2
Hay un problema. ¿Qué pasa si quiero solicitar algo más que identificación y texto? Lo he intentado, pero la opción de datos solo obtiene los datos de templateResult y los ignoro para templateSelection. ¿Alguna idea?
ClearBoth
2
Además, probé esta solución, pero desafortunadamente después de llamar, $select.trigger('change');la opción todavía tiene el texto Cargando ... , aunque data.texttenía exactamente el texto que quería. Quizás esto ya no sea compatible con 4.0.6 .
Alisson
28

Lo que hice es más limpio y necesito hacer dos matrices:

  • uno contiene una lista de Object con id y un texto (en mi caso, su id y nombre, dependiendo de su templateResult) (es lo que obtiene de la consulta ajax)
  • el segundo es solo una matriz de identificadores (valor de selección)

Inicializo select2 usando la primera matriz como datos y la segunda como val.

Una función de ejemplo con como parámetros un dict de id: name.

function initMyList(values) {
    var selected = [];
    var initials = [];

    for (var s in values) {
        initials.push({id: s, name: values[s].name});
        selected.push(s);
    }

    $('#myselect2').select2({
        data: initials,
        ajax: {
            url: "/path/to/value/",
            dataType: 'json',
            delay: 250,
            data: function (params) {
                return {
                    term: params.term,
                    page: params.page || 1,
                };
            },
            processResults: function (data, params) {
                params.page = params.page || 1;

                return {
                    results: data.items,
                    pagination: {
                        more: (params.page * 30) < data.total_count
                    }
                };
            },
            cache: true
        },
        minimumInputLength: 1,
        tokenSeparators: [",", " "],
        placeholder: "Select none, one or many values",
        templateResult: function (item) { return item.name; },
        templateSelection: function (item) { return item.name; },
        matcher: function(term, text) { return text.name.toUpperCase().indexOf(term.toUpperCase()) != -1; },
    });

    $('#myselect2').val(selected).trigger('change');
}

Puede alimentar el valor de las iniciales usando llamadas ajax y usar jquery promises para hacer la inicialización select2.

firebird631
fuente
3
Esta es definitivamente la mejor manera de hacerlo. Manipular el DOM agregando <option>no parece ser la forma más lógica de hacerlo. Así es como lo hago en 4.0
jiaweizhang
2
@ firebird631, su solución es brillante, funciona y muchas gracias!
userlond
esta es la solución más limpia que he visto hasta ahora
Jimmy Ilenloa
Notaré que internamente la dataopción es solo generar <option>etiquetas y agregarlas a la selección. Esta respuesta no cubre cómo maneja los casos en los que necesita ir a una API para obtener más información, pero supongo que ese es un caso menos típico.
Kevin Brown
Hola con ajax no puedo hacerlo. Cuando envío una lista con dos objetos, no hay problema, intenta insertar esos dos objetos pero solo uno insertado. y ya seleccionado automáticamente. Nunca pude ver dos opciones, incluso envié dos elementos.
Sahin Yanlık
7

Funciona para mí ...

No use jQuery, solo HTML: cree el valor de opción que mostrará como seleccionado . Si el ID está en los datos de select2 , se seleccionará automáticamente.

<select id="select2" name="mySelect2">
  <option value="mySelectedValue">
        Hello, I'm here!
  </option>
</select>

Select2.org - Valores predeterminados preseleccionados

nat_jea
fuente
6

El problema de trigger('change')verme obligado a volverme loco, ya que tenía un código personalizado en el changeactivador, que solo debería activarse cuando el usuario cambia la opción en el menú desplegable. En mi opinión, el cambio no debe activarse al establecer un valor de inicialización al inicio.

Cavé profundo y encontré lo siguiente: https://github.com/select2/select2/issues/3620

Ejemplo:

$dropDown.val(1).trigger('change.select2');

Adamj
fuente
4

Un escenario que no he visto que la gente realmente responda, es cómo tener una preselección cuando las opciones provienen de AJAX y puede seleccionar varias. Dado que esta es la página de acceso para la preselección de AJAX, agregaré mi solución aquí.

$('#mySelect').select2({
    ajax: {
        url: endpoint,
        dataType: 'json',
        data: [
            { // Each of these gets processed by fnRenderResults.
                id: usersId,
                text: usersFullName,
                full_name: usersFullName,
                email: usersEmail,
                image_url: usersImageUrl,
                selected: true // Causes the selection to actually get selected.
            }
        ],
        processResults: function(data) {

            return {
                results: data.users,
                pagination: {
                    more: data.next !== null
                }
            };

        }
    },
    templateResult: fnRenderResults,
    templateSelection: fnRenderSelection, // Renders the result with my own style
    selectOnClose: true
}); 
CafeínaCódigoMono
fuente
3

Si está utilizando templateSelection y ajax, es posible que algunas de estas otras respuestas no funcionen. Parece que la creación de un nuevo optionelemento y la configuración de valuey textno satisfará el método de la plantilla cuando sus objetos de datos usen otros valores además de id y text.

Esto es lo que funcionó para mí:

$("#selectElem").select2({
  ajax: { ... },
  data: [YOUR_DEFAULT_OBJECT],
  templateSelection: yourCustomTemplate
} 

Echa un vistazo a jsFiddle aquí: https://jsfiddle.net/shanabus/f8h1xnv4

En mi caso, tuve que processResultsingresar ya que mis datos no contenían los campos obligatorios idy text. Si necesita hacer esto, también deberá ejecutar su selección inicial a través de la misma función. Al igual que:

$(".js-select2").select2({
  ajax: {
    url: SOME_URL,
    processResults: processData
  },
  data: processData([YOUR_INIT_OBJECT]).results,
  minimumInputLength: 1,
  templateSelection: myCustomTemplate
});

function processData(data) {
  var mapdata = $.map(data, function (obj) {      
    obj.id = obj.Id;
    obj.text = '[' + obj.Code + '] ' + obj.Description;
    return obj;
  });
  return { results: mapdata }; 
}

function myCustomTemplate(item) {
     return '<strong>' + item.Code + '</strong> - ' + item.Description;
}
shanabus
fuente
¡¡¡¡¡FINALMENTE!!!!! ¡¡Vaya, eso no podría haber sido más frustrante !! Había estado usando "nombre" en lugar de "texto" y me estaba matando. Cambié a la tecla "texto" predeterminada y ahora funciona perfectamente. ¡Gracias!
HTMLGuy
1

después de pasar unas horas buscando una solución, decidí crear la mía propia. El es:

 function CustomInitSelect2(element, options) {
            if (options.url) {
                $.ajax({
                    type: 'GET',
                    url: options.url,
                    dataType: 'json'
                }).then(function (data) {
                    element.select2({
                        data: data
                    });
                    if (options.initialValue) {
                        element.val(options.initialValue).trigger('change');
                    }
                });
            }
        }

Y puede inicializar las selecciones usando esta función:

$('.select2').each(function (index, element) {
            var item = $(element);
            if (item.data('url')) {
                CustomInitSelect2(item, {
                    url: item.data('url'),
                    initialValue: item.data('value')
                });
            }
            else {
                item.select2();
            }
        });

Y, por supuesto, aquí está el html:

<select class="form-control select2" id="test1" data-url="mysite/load" data-value="123"></select>
Francinaldo Portela
fuente
Gran idea para un procedimiento de inicialización genérico.
Carlos Mora
1

Para una solución simple y semántica, prefiero definir el valor inicial en HTML, ejemplo:

<select name="myfield" data-placeholder="Select an option">
    <option value="initial-value" selected>Initial text</option>
</select>

Entonces, cuando llamo $('select').select2({ ajax: {...}});al valor inicial es initial-valuey su texto de opción es Initial text.

Mi versión actual de Select2 es 4.0.3 , pero creo que tiene una gran compatibilidad con otras versiones.

Marcio Mazzucato
fuente
Supongo que está guardando no solo un valor seleccionado, sino también un texto en su base de datos, para que pueda recuperarlo así ... ¡sheesh!
ITDesigns.eu
@ ITDesigns.eu, no lo creo, el valor sento a la base de datos es initial-valuey el texto Initial textactúa como un marcador de posición, no se envía a la base de datos. Está funcionando en mi aplicación.
Marcio Mazzucato
¿Por qué necesitaría un marcador de posición en lugar de un texto real en la opción real ?
ITDesigns.eu
@ ITDesigns.eu, Select2 captura esta información y se monta a sí mismo
Marcio Mazzucato
1

https://github.com/select2/select2/issues/4272 solo esto resolvió mi problema. Incluso si establece el valor predeterminado por opción, debe formatear el objeto, que tiene el atributo de texto y esto es lo que desea mostrar en su opción. por lo tanto, su función de formato debe usar ||para elegir el atributo que no está vacío.

王佳 颉
fuente
0

¡Estoy agregando esta respuesta principalmente porque no puedo comentar arriba! Descubrí que fue el comentario de @Nicki y su jsfiddle, https://jsfiddle.net/57co6c95/ , lo que finalmente hizo que esto funcionara para mí.

Entre otras cosas, dio ejemplos del formato necesario para el json. El único cambio que tuve que hacer fue que mis resultados iniciales se devolvieron en el mismo formato que la otra llamada ajax, así que tuve que usar

$option.text(data[0].text).val(data[0].id);

más bien que

$option.text(data.text).val(data.id);
jogilder
fuente
0

Cree un combo ajax simple con el valor seleccionado inicial para select2 4.0.3

<select name="mycombo" id="mycombo""></select>                   
<script>
document.addEventListener("DOMContentLoaded", function (event) {
    selectMaker.create('table', 'idname', '1', $("#mycombo"), 2, 'type');                                
});
</script>  

biblioteca .js

var selectMaker = {
create: function (table, fieldname, initialSelected, input, minimumInputLength = 3, type ='',placeholder = 'Select a element') {
    if (input.data('select2')) {
        input.select2("destroy");
    }
    input.select2({
        placeholder: placeholder,
        width: '100%',
        minimumInputLength: minimumInputLength,
        containerCssClass: type,
        dropdownCssClass: type,
        ajax: {
            url: 'ajaxValues.php?getQuery=true&table=' + table + '&fieldname=' + fieldname + '&type=' + type,
            type: 'post',
            dataType: 'json',
            contentType: "application/json",
            delay: 250,
            data: function (params) {
                return {
                    term: params.term, // search term
                    page: params.page
                };
            },
            processResults: function (data) {
                return {
                    results: $.map(data.items, function (item) {
                        return {
                            text: item.name,
                            id: item.id
                        }
                    })
                };
            }
        }
    });
    if (initialSelected>0) {
        var $option = $('<option selected>Cargando...</option>').val(0);
        input.append($option).trigger('change'); // append the option and update Select2
        $.ajax({// make the request for the selected data object
            type: 'GET',
            url: 'ajaxValues.php?getQuery=true&table=' + table + '&fieldname=' + fieldname + '&type=' + type + '&initialSelected=' + initialSelected,
            dataType: 'json'
        }).then(function (data) {
            // Here we should have the data object
            $option.text(data.items[0].name).val(data.items[0].id); // update the text that is displayed (and maybe even the value)
            $option.removeData(); // remove any caching data that might be associated
            input.trigger('change'); // notify JavaScript components of possible changes
        });
    }
}
};

y el lado del servidor php

<?php
if (isset($_GET['getQuery']) && isset($_GET['table']) && isset($_GET['fieldname'])) {
//parametros carga de petición
parse_str(file_get_contents("php://input"), $data);
$data = (object) $data;
if (isset($data->term)) {
    $term = pSQL($data->term);
}else{
    $term = '';
}
if (isset($_GET['initialSelected'])){
    $id =pSQL($_GET['initialSelected']);
}else{
    $id = '';
}
if ($_GET['table'] == 'mytable' && $_GET['fieldname'] == 'mycolname' && $_GET['type'] == 'mytype') {

    if (empty($id)){
        $where = "and name like '%" . $term . "%'";
    }else{
         $where = "and id= ".$id;
    }

    $rows = yourarrayfunctionfromsql("SELECT id, name 
                    FROM yourtable
                    WHERE 1 " . $where . "
                    ORDER BY name ");
}

$items = array("items" => $rows);
$var = json_encode($items);
echo $var;
?>
Alejandro Aranda
fuente
-4

Hola, casi salgo de esto y regresa para seleccionar 3.5.1. ¡Pero finalmente obtuve la respuesta!

$('#test').select2({
    placeholder: "Select a Country",
    minimumResultsForSearch: 2,
    ajax: {
        url: '...',
        dataType: 'json',
        cache: false,
        data: function (params) {
                var queryParameters = {
                    q: params.term
                }
                return queryParameters;
        },
        processResults: function (data) {
            return {
                results: data.items
            };
        }
    }
});

var option1 = new Option("new",true, true);

$('#status').append(option1);

$('#status').trigger('change');

Solo asegúrese de que la nueva opción sea una de las opciones select2. Consigo esto por un json.

usuario3163404
fuente
Hola, ¿qué es '#status'? ¿Debería ser '#prueba'?
user2808054