It’s another one of those days when all I want to do is work on something cool. This time I was thinking about creating reusable and scalable support for multi-selection, where users will be able to select or unselect a range of items using Shift + Click.

Let’s see what I’ve come up with. First, we need to create an interface that everyone who wants to use this functionality must implement:

Now, let’s create a box component that implements the SelectionItem interface:

Next, we’ll create a class named MultiSelection that receives a QueryList of items that implement the SelectionItem interface. This class will manage the active state of each item, including range selection support by using Shift + Click. Here’s how we’re going to use it:

We create a new instance of MultiSelection and pass one argument — the boxes property, which holds a reference to a BoxComponent QueryList. Now, let’s start implementing the MultiSelection class:

We start by iterating over the items and uses the fromEvent observable to map each item to an observable that fires when the user clicks on the element. We also need to have the index, the component instance, and an indication of whether the user is holding the Shift key. We’ll see in a second how we make use of this information.

Now that we have a collection of streams, we can use the merge observable, which will emit whenever the user clicks on the element:

When users click on an item, we first check if they are holding the Shift button. If that’s not the case, we use the corresponding method based on the current status of the item. We also save the item index as the last index the user chose. Now, let’s move on to the second part, when the Shift button is pressed:

Here we perform some simple math and calculate the range of items we need to select based on the current and the last item indexes. Then, we loop over each item in the range and invoke the corresponding method based on the current item’s active status.

Demo

Now, let’s add an API, which returns the active items both as a getter and an observable:

Note that there are more performant ways to achieve the same result, but for the sake of the article, I prefer to keep it simple.

Multi Checkbox Support

Now that we have the core functionality working, let’s see how we can adapt it to other features in our application. Let’s make it work with Angular Reactive Forms and multiple checkbox selection.

First, let’s create a form so that we have something to work with:

Now, let’s implement the MultiSelectionCheckbox directive:

The MultiSelectionCheckbox directive implements the SelectionItem interface. We need access to the underline control, so we use DI the obtain a reference to NgControl , which holds the control instance. Now that we have the control, it’s just a matter of patching its value based on the status of the item determined by the MultiSelect instance.

Multi Checkbox Demo

It’s as simple as that. You can play with the code in ng-run. I also added a skipPredicate , and items replacements support.

🚀 In Case You Missed It

Here are a few of my open source projects:

Akita : State Management Tailored-Made for JS Applications

: State Management Tailored-Made for JS Applications Spectator : A Powerful Tool to Simplify Your Angular Tests

: A Powerful Tool to Simplify Your Angular Tests Transloco : The Internationalization library Angular

The Internationalization library Angular Forms Manager : The Foundation for Proper Form Management in Angular

: The Foundation for Proper Form Management in Angular Cashew: A flexible and straightforward library that caches HTTP requests

Follow me on Medium or Twitter to read more about Angular, Akita and JS!