AngularJS multiselect dropdown (updated)

Home / AngularJS multiselect dropdown (updated)

angular_small
I was playing around with my trusty multi-select dropdown earlier today and a colleague of mine pointed out that changing the selected items outside of the directive would not actually deselect previously selected items. This was a curious bug since I knew I had a watcher on the ngModel selections.


Setting the selected items on first load always worked without a hitch. But I found that I had put in some conditional logic that was not firing the “mark checked” code that I had in place. The “mark checked” code would simply iterate over all of the internal items and compare their values to ngModel, whether it was simple scalar or complex object, and set the checked property. The comparison itself wasn’t comparing apples to apples, though, and nothing was being checked. Additionally, clearing out ngModel did nothing. Finally, since ngModel would change inside of the directive itself when calling modelCtrl.$setViewValue, I wanted to avoid the looping over the items and ngModel array unnecessarily.

I won’t post a ton of code here showing the changes, but suffice it to say there is a bit set when the directive itself has triggered a change to the ngModel. When this bit is set, the iterative code that sets the checkmarks is not called when the ngModel watcher is triggered. Conversely, if an external process updates ngModel, the code will fire to compare the new selection vs. the available items and mark them as checked or unchecked appropriately.

The updated demo shows how randomly setting the selected items or clearing the selecting items stored in ngModel will now be properly handled by the directive.

The full source can be seen on Github.
There’s a plunk if you prefer Plunker and pen if your prefer Codepen.

8 thoughts on “AngularJS multiselect dropdown (updated)”

    1. It should work.

      The basic premise is that you have options defined with key/value:

      options="p.key as p.value for p in vm.options1"
      

      By default, the item, has top-level properties only of checked, key, and value. However, the true underlying model is attached as an object named model. Therefore, if, for example, I were to change the demo options (vm.options1) to add a random number to the underlying model like so:

      for (var i = 0; i < 10; i++) {
          vm.options1.push({ key: i + 1, value: 'Prop' + (i + 1).toString(), randnum: getRandomInt(1, 1000) });
      }
      

      Then the property is accessible via the “order-label” by specifying like this. order-label=”model.randnum”. Here’s how that would look in the mark-up:

      <multiselect class="input-xlarge multiselect" ng-model="vm.option1" options="p.key as p.value for p in vm.options1" select-limit="5"
                   header="Select Stuff1" selected-header="options selected" multiple="true"
                   enable-filter="true" filter-placeholder="Filter stuff.." required
                   order-label="model.randnum">
      </multiselect>
      

      Hope that helps ..

      1. The general paradigm for loading a dropdown in Angular is to use “options” (or ng-options) to create, effectively, a dictionary of key value pairs to display in the dropdown.

        Within the mark-up of the demo(s), then, you’ll see this a lot:

        options="p.key as p.value for p in vm.options1"
        

        Here, any key/value combination can be extracted from your list of objects. If we look at the source code for how vm.options1 is populated, it looks like this:

        vm.options1 = [];
        for (var i = 0; i < 10; i++) {
            vm.options1.push({ key: i + 1, value: 'Prop' + (i + 1).toString() });
        }
        

        If you wanted to use your own properties, it might look something like this:

        vm.options1 = [
         { myKey: 1, myValue: "This is my first value" },
         { myKey: 2, myValue: "This is my second value" },
         { myKey: 3, myValue: "This is my third value" },
         { myKey: 4, myValue: "This is my fourth value" }
        ];
        

        And if we wanted to display those values, it would look like this in the mark-up:

        <multiselect class="input-xlarge multiselect" ng-model="vm.option1" options="p.myKey as p.myValue for p in vm.options1" select-limit="5"
                     header="Select Stuff1" selected-header="options selected" multiple="true" required
                     enable-filter="true" filter-placeholder="Filter stuff..">
        </multiselect>
        

        Finally, we’d get something like this displayed:

        Hope that helps..

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.