Implement Multiple Selection Using Shift + Click in Angular
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.
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.
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
- Spectator: A Powerful Tool to Simplify Your Angular Tests
- Transloco: The Internationalization library Angular
- Forms Manager: 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!