javascript – How do I put the text of the<optgroup> before the<option> when selecting it with select2?

Question:

What I intend to do is that when selecting one of the options, the text of the parent (optgroup) is placed and it shows me something like " LG / Lg – Test 1 " (notice in the example that I put that "LG" is the parent (optgroup ) and "Lg – Test 1" is the child (option)) and I want to make it so that the user can see which parent (optgroup) they are selecting.

In the example that I place, the formatDataSelection function is the one that is in charge of placing the text when selecting an option.

Here is 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" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/js/select2.min.js"></script>

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

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

$('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" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/js/select2.min.js"></script>

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

Answer:

If you look at the DOM that is created when generating your second script, it is not the typical one with optgroup but 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 are not referring to the optgroup (as you currently have it in your example) but to the ul .

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

Later, and seeing how the DOM takes on the structure once the dropdown is generated, I am going to scale two positions using .parent().parent() to get to the li , although I am going to filter so that I only get the elements with role="group" by doing the filter .find("[role='group']") .

Lastly, I'll get the prevObject element to detect which one is before the element we just selected and I'll get its aria-label attribute.

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 is seen in a much more "friendly" way .

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" />
<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 deselecting an element that has already been selected, since it causes problems that it cannot obtain the getAttribute of a null element.

Since both creating and deleting an element from the dropdown uses the same function, I have created a new condition that only uses the getAttribute function 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've edited the example above so you can see that it no longer throws an error when deselecting dropdown items.

Scroll to Top