bootstrap-select icon indicating copy to clipboard operation
bootstrap-select copied to clipboard

Ability to select/deselect all optgroup items

Open jayanthigopi opened this issue 11 years ago • 34 comments

I need to clickable optgroup option, when we click on the optgroup label to select / deselect what are all items having inside the groups are selected /deselected. This is very urgent please...

jayanthigopi avatar Sep 16 '14 07:09 jayanthigopi

Hello Silvio,

Is there any chance that we get some help about this? Thanks.

tomicakorac avatar Dec 08 '14 10:12 tomicakorac

Any support regarding this?

adic26 avatar Dec 23 '14 16:12 adic26

Looking forward to this functionality too.

Also, duplicate of #680, in which @t0xicCode tagged this functionality for the 1.7.0 milestone.

eswak avatar Apr 01 '15 15:04 eswak

It would be nice and seems logical.

rkurbatov avatar Apr 15 '15 08:04 rkurbatov

Looking forward for that.

godjan avatar Apr 29 '15 18:04 godjan

For anybody who truly needs this right now, replace these lines here: https://github.com/silviomoreto/bootstrap-select/blob/master/js/bootstrap-select.js#L957-L965 with

      this.$menu.on('click', 'li.divider, li.dropdown-header', function (e) {
        e.preventDefault();
        e.stopPropagation();

          if (that.multiple && $(e.currentTarget).hasClass('dropdown-header')) {
              var $options = that.$element.find('option'),
                  $optgroup = $(e.currentTarget),
                  optgroupID = $optgroup.data('optgroup'),
                  $optgroupLis = that.$lis.filter('[data-optgroup="' + optgroupID + '"]').not('.divider, .dropdown-header, .disabled, .hidden'),
                  selectAll = $optgroupLis.filter('.selected').length !== $optgroupLis.length;

              if (!$options.eq($optgroupLis.data('originalIndex')).parent().data('maxOptions')) {
                  $optgroupLis.each(function(index) {
                      var $this = $(this);
                      $options.eq($this.data('originalIndex')).prop('selected', selectAll);
                  });

                  $optgroupLis.toggleClass('selected', selectAll);

                  that.render(false);
              }
          }

        if (that.options.liveSearch) {
          that.$searchbox.focus();
        } else {
          that.$button.focus();
        }
      });

I was going to implement this, but thought it would be best to first discuss how best to handle optgroups with maxOptions set. With this code, if an optgroup has maxOptions set, clicking the optgroup label doesn't do anything. Anybody have any ideas for a different way to handle this?

caseyjhol avatar Apr 30 '15 22:04 caseyjhol

I agree with your suggestion. I don't see a better way to deal with the limited number of options. Maybe it's also a good idea to add some kind of notification in that case, so that users are also aware of it?

tomicakorac avatar May 01 '15 07:05 tomicakorac

@caseyjhol :+1: thanks a lot :-)

eswak avatar May 05 '15 14:05 eswak

Hi @caseyjhol, Are there a new solution for this? I know that now we have a new version Selectpicker.VERSION = '1.7.2'; Or someone else have another solution for this? thanks a lot:)

lisandro444 avatar Aug 18 '15 01:08 lisandro444

+1 for this

Khrysller avatar Nov 01 '15 01:11 Khrysller

@caseyjhol, can you reconfirm the lines to be replaced for the actual version? I really would like to try this feature. And for the conflict, maybe the best option should be document it. There some of this kinda of stuff on Bootstrap and they just document it. Well this being said there is not a practical solution that don't create a conflict with another feature. Thanks.

Khrysller avatar Nov 13 '15 02:11 Khrysller

+1 In regard to how to deal with maxOptions... just check the first maxOptions items.

weatherbell avatar Aug 02 '16 18:08 weatherbell

@caseyjhol Hey, I've got the majority of this working on a fork at the moment. The only thing I have left to do is fix some CSS a bit so it looks clickable and add to the documentation.


Brief explanation of maxOptions problem

When I first read this, I didn't fully understand the issue of maxOptions until I started building it and ran into the same issue. So this is a brief explanation to catch up anyone else who needs it:

  • If you use the data-max-options="3" attribute to the <optgroup> it will only let you select three options, even if you have more.
  • If you add the ability to select all options under an <optgroup> that has a max options that is less than the total options, which options do you choose to select?

The current functionality I have working is the follows:

Selects the first few if maxOptions is set

I incorporated the idea from @weatherbell to select the first maxOptions. I like this idea a lot because if the user is confused, the first thing the user will try to do is select the next few which will then force the warning: Group limit reached ({n} item max). So any confusion is self-fixing.

It's a data attribute

In order for the programmer to add the "select header" functionality, they must add the data-select-all-headers="true" attribute to their <select> like so:

<select id="selectHeader" class="selectpicker form-control" multiple data-select-all-headers="true" title="Selecting a header will select all it can.">
<!-- Options with groups -->
</select>

Requiring this to be opt-in solves a number of issues. It solves the point made by @Khrysller that we should document it. By requiring it to be opt-in, we force the programmer to read the documentation explaining the oddities caused by maxOptions. It also puts both the burden and freedom of usability on the programmer.


I'm going to continue working on the documentation and css unless anyone has any disagreements with this plan.

Flaque avatar Aug 17 '16 19:08 Flaque

Note that the existing Select All ability currently ignores the maxOptions. In practice, it doesn't really make sense to have a "select all" while at the same time having a limit on the number of options you can select. I highly doubt anyone would be trying to use both these features at once, so going to extra lengths to make them somehow work together may not be particularly helpful.

Personally I would say the sensible thing to do is either ignore the maxOptions (as it currently does for the main select all) or force the select all option off if maxOptions is defined.

andrews05 avatar Sep 15 '16 00:09 andrews05

I had problems with the above code and the function "change". Solved by adding "onselect" directly on the tag <select>:

<select class="form-control selectpicker" name="mynames[]"
        multiple="multiple" onchange="changeSelectPicker(this)">
    <option value="1">Count 1</option>
    <option value="2">Count 2</option>
    <option value="3">Count 3</option>
    <option value="4">Count 4</option>
</select>
// PROBLEM
$('.selectpicker').on("changed.bs.select", function (e, clickedIndex) {
    console.log(this); // NO RETURN
});
// PROBLEM
$('.selectpicker').on("change", function (this) {
    console.log(this); // NO RETURN
});
// SOLVED
function changeSelectPicker(that) {
    var valuesArr = $(that).val();
    console.log(valuesArr); // return selected values ["1", "3", "4"]
}

juliorosseti avatar Oct 17 '16 13:10 juliorosseti

@Flaque Have you done anything else with this? Can you post what you have?

KrunchMuffin avatar Feb 13 '17 16:02 KrunchMuffin

Sorry, @KrunchMuffin. I haven't done anything more than the original PR. I guess I missed this. I might be able to look into it, but no promises.

Flaque avatar Feb 13 '17 16:02 Flaque

Ah, sorry, didn't see the PR. What I am really after is the ability to use Select All while minding the maxOptions. I may be able to do something with your code.

KrunchMuffin avatar Feb 13 '17 16:02 KrunchMuffin

Any plans on integrating these changes? Is there a workaround available to gain this functionality or at least trap the click event on the optgroup?

mikenorm412 avatar Aug 23 '17 14:08 mikenorm412

For actual version this works for me, simply add this:

$("li.dropdown-header").click(function(e){
        e.preventDefault();
        e.stopPropagation();

          if (that.multiple && $(e.currentTarget).hasClass('dropdown-header')) {
              var $options = that.$element.find('option'),
                  $optgroup = $(e.currentTarget),
                  optgroupID = $optgroup.data('optgroup'),
                  $optgroupLis = that.$lis.filter('[data-optgroup="' + optgroupID + '"]').not('.divider, .dropdown-header, .disabled, .hidden'),
                  selectAll = $optgroupLis.filter('.selected').length !== $optgroupLis.length;

              if (!$options.eq($optgroupLis.data('originalIndex')).parent().data('maxOptions')) {
                  $optgroupLis.each(function(index) {
                      var $this = $(this);
                      $options.eq($this.data('originalIndex')).prop('selected', selectAll);
                  });

                  $optgroupLis.toggleClass('selected', selectAll);

                  that.render(false);
              }
          }

        if (that.options.liveSearch) {
          that.$searchbox.focus();
        } else {
          that.$button.focus();
        }
      });

in line 1358 (https://github.com/silviomoreto/bootstrap-select/blob/master/js/bootstrap-select.js#L1358)

jerearaujo03 avatar Mar 06 '18 22:03 jerearaujo03

@caseyjhol Are there any solutions to achieve the same in the version compatible with bootstrap-4.0 i.e. bootstrap-select-v1.13.0-beta version?

mjain1994 avatar Apr 03 '18 09:04 mjain1994

I am also interested in this feature for bootstrap-4.0 i.e. bootstrap-select-v1.13.0-beta version.

oabhishek avatar Apr 04 '18 22:04 oabhishek

jerearaujo03 thanks for sharing - your code works fine if you have ONE selectlist on your page. If you have more, this works for me:

this.$menuInner.find( "li.dropdown-header" ).each(function(index) {
	$(this).on('click',function(e) {
		e.preventDefault();
		e.stopPropagation();

		if (that.multiple && $(e.currentTarget).hasClass('dropdown-header')) {
			var $options = that.$element.find('option'),
			$optgroup = $(e.currentTarget),
			optgroupID = $optgroup.data('optgroup'),
			$optgroupLis = that.$lis.filter('[data-optgroup="' + optgroupID + '"]').not('.divider, .dropdown-header, .disabled, .hidden'),
			selectAll = $optgroupLis.filter('.selected').length !== $optgroupLis.length;
			
			if (!$options.eq($optgroupLis.data('originalIndex')).parent().data('maxOptions')) {
				$optgroupLis.each(function(index) {
					var $this = $(this);
					$options.eq($this.data('originalIndex')).prop('selected', selectAll);
				});
				$optgroupLis.toggleClass('selected', selectAll);
				that.render(false);
			}
		}
		if (that.options.liveSearch) {
			that.$searchbox.focus();
		} else {
			that.$button.focus();
		}
	});
});

mediafant avatar Apr 22 '18 16:04 mediafant

Great!

jerearaujo03 avatar Apr 22 '18 18:04 jerearaujo03

It's not work for me ...

agencefacton avatar Aug 01 '18 15:08 agencefacton

The previous posted solution does not work on the current version which has a different code base. Can we have a updated solution or a built in feature for toggling select all items under an option group please? Will pay/donate for this!

flintcreativestudio avatar Mar 13 '19 08:03 flintcreativestudio

Someone provided a solution that worked for me: https://stackoverflow.com/questions/41821115/select-deselect-optgroup-based-on-option-select-in-select-picker-boostrap

CLothering avatar Mar 15 '19 13:03 CLothering

I did this, just add this bottom of the page:

$(".dropdown-header").each(function (index, header) {

        var header = $(header);

        header.click(function () {
            var dataoptgroup = $(this).attr("data-optgroup");

            var group_lis = $('li[data-optgroup=' + dataoptgroup + ']').filter('li[data-original-index]');

            group_lis.each(function (index, option) {
                $(option).find("a").click()
            });
        });
    });

ahp98 avatar Oct 10 '19 09:10 ahp98

I did this, just add this bottom of the page:

$(".dropdown-header").each(function (index, header) {

        var header = $(header);

        header.click(function () {
            var dataoptgroup = $(this).attr("data-optgroup");

            var group_lis = $('li[data-optgroup=' + dataoptgroup + ']').filter('li[data-original-index]');

            group_lis.each(function (index, option) {
                $(option).find("a").click()
            });
        });
    });

v1.13.12 works for me:

add this in this.$menuInner.on('click', '.divider, .dropdown-header', function (e) line 2521:

if(that.multiple){ var group_id= $(this).attr("class").split(' ')[1]; $("li."+group_id+' a').trigger('click')};

seba-salavilla avatar Feb 26 '20 20:02 seba-salavilla

Hi, i really need that too. There is solutions but no PR ?

Erwane avatar Jun 18 '20 09:06 Erwane