How do I place the optgroup text before the option when selecting it with select2?

4

What I intend to do is that when selecting one of the options, put the parent text (optgroup) and show me something like " LG / Lg - Test 1 " (notece in the example that I say that "LG" is the parent (optgroup) and "Lg - Test 1" is the child (option)) and I want to do so that the user can see which parent (optgroup) is selecting.

In the example that I put the function formatDataSelection is the one that is responsible for placing the text when selecting an option.

Below the code of what I have:

var data = {"results":[{"text":"LG","children":[{"id":1,"text":"Lg - Prueba 1"},{"id":2,"text":"Lg - Prueba 2"}]},{"text":"Samsung","children":[{"id":3,"text":"Sam - Prueba 1"},{"id":4,"text":"Sam - Prueba 2"}]}],"pagination":{"more":false}};


$("select").select2({
  placeholder: "Elija...",
  allowClear: true,
  data: data.results,
  escapeMarkup: function (markup) { return markup; },
  templateResult: formatData,
  templateSelection: formatDataSelection
});
function formatData (data) {
  if (data.loading) return data.text;
  return data.text;
}
function formatDataSelection (data) {
  return data.text;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/js/select2.min.js"></script>

<select style="width: 100%"></select>

The response that the user @JuankGlezz gave me, led me to this other problem: The text of the parent label of the selected option is not available.

$('select').select2({
    placeholder: "Elija...",
    allowClear: true,
    ajax: {
    	type: "GET",
        dataType: 'json',
        url: 'https://api.myjson.com/bins/12apr1',
        delay: 250,
        data: function(params) {
            return {
	            term: params.term, // search term
	            page: params.page || 1, //page number
            }
        },
	    processResults: function (data, page) {
	        return {
                results: $.map(data.results, function (n) {
                    return {
                        text: n.text,
                        children: n.children
                    }
                }),
	            //results: data.results,
	            pagination: {
	                more: data.pagination.more,
	            }
	        };
	    },
        cache: true
    },
    escapeMarkup: function (markup) { return markup; },
    templateResult: crear_marca_modelo_formatData,
    templateSelection: crear_marca_modelo_formatDataSelection
});
function crear_marca_modelo_formatData (data) {
	if (data.loading) return data.text;
	return data.text;
}
function crear_marca_modelo_formatDataSelection (data) {
	let labelOptg = $(data.element).parent().attr('label');
	return labelOptg + " / " + data.text;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/js/select2.min.js"></script>

<select style="width: 100%"></select>
    
asked by Pablo Contreras 09.05.2017 в 20:56
source

2 answers

4

If you look at the DOM that is created when generating your second script, it is not the typical one with optgroup if not that it creates one with the following form:

<li .... role="group" aria-label="Life Good">
    <strong ...> Life Good </strong>
    <ul ....>
        <li ....> Lg Prueba 1 </li>
        <li ....> Lg Prueba 2 </li>
    </ul>
</li>

Therefore, when referring to the parent element, you do not reference the optgroup (as you currently have it in your example), but the ul .

I have also noticed that each of the elements of the select when you select them get a certain class called .select2-results__option--highlighted , so I used this class to refer to the element we just selected.

Subsequently, and seeing how the structure takes the DOM once the dropdown is generated, I will climb two positions by .parent().parent() to reach the li although I will filter that I only get the elements with role="group" doing the filter .find("[role='group']") .

Finally, I will obtain the prevObject element to detect which is the previous one to the element that we have just selected and I will obtain its attribute aria-label .

Additionally: I have also made a condition so that if there is no element that has been selected, undefined... / Elija... does not appear and only the word Elija... appears so that it looks much more "friendly".

Your modified example:

$('select').select2({
    placeholder: "Elija...",
    allowClear: true,
    ajax: {
    	type: "GET",
        dataType: 'json',
        url: 'https://api.myjson.com/bins/12apr1',
        delay: 250,
        data: function(params) {
            return {
	            term: params.term, // search term
	            page: params.page || 1, //page number
            }
        },
	    processResults: function (data, page) {
	        return {
                results: $.map(data.results, function (n) {
                    return {
                        text: n.text,
                        children: n.children
                    }
                }),
	            //results: data.results,
	            pagination: {
	                more: data.pagination.more,
	            }
	        };
	    },
        cache: true
    },
    escapeMarkup: function (markup) { return markup; },
    templateResult: crear_marca_modelo_formatData,
    templateSelection: crear_marca_modelo_formatDataSelection
});
function crear_marca_modelo_formatData (data) {
	if (data.loading) return data.text;
	return data.text;
}
function crear_marca_modelo_formatDataSelection (data) {
  if(data.element !== undefined){
    if($(".select2-results__option--highlighted").parent().parent().find("[role='group']").prevObject[0]){
         var etiquetaOptGroup = $(".select2-results__option--highlighted").parent().parent().find("[role='group']").prevObject[0].getAttribute("aria-label");
	 return etiquetaOptGroup + " / " + data.text;
    }
  }else{
    let labelOptg = $(data.element).parent().attr('label');
	  return data.text;
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/js/select2.min.js"></script>

<select style="width: 100%"></select>

UPDATE: According to the comments the problem is in deselecting an element that has already been selected since it gives problems that it can not get the getAttribute of a null element.

As much to create as to erase an element of the pulldown uses the same function I have created a new condition that only use the function getAttribute if the object exists to solve the error:

if(data.element !== undefined){
    if($(".select2-results__option--highlighted").parent().parent().find("[role='group']").prevObject[0]){
         //Aqui obtiene el atributo cuando el objeto existe y lo añade al desplegable
    }
}else{
    //Código cuando data viene como undefined
}

In this way we would solve the error. I have edited the previous example so that it can be seen that it no longer produces any errors when deselecting the elements of the drop-down.

    
answered by 04.06.2017 / 15:13
source
0

It's just a matter of modifying the templateSelection function a bit, because in the data that the function receives it also contains an attribute element which you can select it with jQuery to later search the father in this case <optiongorup> , then the function would be as follows:

function formatDataSelection(data) {
  //Debido a que en data recibimos el elemento seleccionado lo buscamos en el DOM 
  //y posteriormente buscamos el padre
  let labelOptg = $(data.element).parent().attr('label');

  //Para devolver lo que tu deseas hay que guardar la etiqueta del Optiongroup 
  //en una variable y retornamos el resultado esperado
  return labelOptg + " / " + data.text;
}

Your working example:

var data = {
  "results": [{
    "text": "LG",
    "children": [{
      "id": 1,
      "text": "Lg - Prueba 1"
    }, {
      "id": 2,
      "text": "Lg - Prueba 2"
    }]
  }, {
    "text": "Samsung",
    "children": [{
      "id": 3,
      "text": "Sam - Prueba 1"
    }, {
      "id": 4,
      "text": "Sam - Prueba 2"
    }]
  }],
  "pagination": {
    "more": false
  }
};


$("select").select2({
  placeholder: "Elija...",
  allowClear: true,
  data: data.results,
  escapeMarkup: function(markup) {
    return markup;
  },
  templateResult: formatData,
  templateSelection: formatDataSelection
})

function formatData(data) {
  if (data.loading) return data.text;
  return data.text;
}

function formatDataSelection(data) {
  //Debido a que en data recibimos el elemento seleccionado lo buscamos en el DOM y posteriormente buscamos el padre
  let labelOptg = $(data.element).parent().attr('label');
  //Para devolver lo que tu deseas hay que gusradar la etiqueta del Optiongroup en una variable y retornamos el resultado esperado
  return labelOptg + " / " + data.text;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/js/select2.min.js"></script>

<select style="width: 100%"></select>
    
answered by 11.05.2017 в 16:54