The problem
Every developer had to struggle with choosing the right plugin/package for things that are time consuming or have already been done in ways that suit their needs. Juggling ๐คนโโ๏ธ between a package that is smaller in size or has certain features that we need can be frustrating.
But what happens when there are no packages to choose from? On top of that, what if there is a feature that millions of developers need, but struggle to find answers or make custom solutions for every app they make.
I am, of course, talking about the atrocious implementation of the multiple select feature built into every major browser ๐ซ
The solution
Since this frustrated my colleagues and me for a long time we decided to make our own implementation and open source it.
We are not pioneers in this, naturally. There is many a plugin trying to solve the same problem. But what stuck with me when researching is none of them seem to quite scratch the perfectionist itch (now always a good thing ๐ ). Some of them require big dependencies like vue.js or jQuery, while some of them just lack the features or the ability to be customized.
Stimulus Multiselect
You are going to need stimulus.js as a dependency for this package to work. It is a minimal, easy to understand javascript flavor for handling HTML.
If you are using a js bundler with node_modules
support (such as esbuild, rollup.js or Webpack) install the package from npm:
yarn add @wizardhealth/stimulus-multiselect
If you're using importmap-rails, you'll need to pin @wizardhealth/stimulus-multiselect
:
./bin/importmap pin @wizardhealth/stimulus-multiselect
Usage
Load your stimulus application as usual and the register the multiselect
controller with it:
import { Application } from '@hotwired/stimulus'
import { Multiselect } from '@wizardhealth/stimulus-multiselect'
const application = Application.start()
application.register('multiselect', Multiselect)
The multiselect has many modes of working. You can fetch data remotely or load a static json collection of items. An example of this static approach:
<div data-controller="multiselect" data-multiselect-items-value='[{ "value": "cuckoo", "text": "Cuckoo ๐ฆ"}, { "value": "macaw", "text": "Macaw ๐ฆ"}, { "value": "rooster", "text": "Rooster ๐"}]' data-placeholder="Search for birds...">
<select multiple="multiple" class="multiselect__hidden" data-multiselect-target="hidden" name="form[test_ids][]" id="form_test_ids"></select>
</div>
Let's build an app
We are gonna build a Rails app (since Stimulus is mostly used with Rails) that has a backend of different sports. We are gonna search through these sports with the help of the multiselect and return the results back to show in the dropdown.
The setup
- Create a new app
rails new multiselect_test
- Scaffold Sports so we can search through them
rails g scaffold Sport name:string
- Migrate it!
rails db:migrate
Add the package
yarn add @wizardhealth/stimulus-multiselect
The implementation
We are going to use the sports#index page for testing. So we need to make the following changes to the controller and view.
- We are going to make use of a simple where for the purposes of this example. The index needs to like like this. The parameter that contains the search term from the select input is inside
params[:q]
# app/controllers/sports_controller.rb
def index
@sports = Sport.where("name LIKE ?", "%#{params[:q]}%")
respond_to do |format|
format.json
format.html
end
end
- The response that the select needs is JSON. So let's change the json response views that are inside
app/views/sports/
# app/views/sports/index.json.jbuilder
json.array! @sports, partial: "sports/sport", as: :sport
The select needs a json that contains the value (usually the id) and the text value (what is shown when selecting)
# app/views/sports/_sport.json.jbuilder
json.value sport.id
json.text sport.name
- Lastly, we need to put the select HTML somewhere. Let's use the
sports/index.html.erb
for this
<!-- app/views/sports/index.html.erb -->
<div data-controller="multiselect" data-multiselect-search-url-value="/sports" data-placeholder="Search for sports...">
<select multiple="multiple" class="multiselect__hidden" data-multiselect-target="hidden" name="sports[]" id="sport_ids"></select>
</div
- Add the contents of this file to your css
/* app/assets/stylesheets/application.css */
// The css contents from the file mention above go HERE
- When going to localhost:3000/sports we should see this
The end product
Now just search any sport you like:
Why Stimulus Multiselect โ
- Easy to set up
- Lightweight
- Good documentation
- Lot of options for loading data and handling selected items
- Design is customizable (css file is extracted so you can make the select suit your application look)
- Accessible
- Contributions and issue raising are welcome on our GitHub page
The finish line ๐
Would love to know what you think. Comment, review the code, try the select and tell us how we can improve ๐
Top comments (1)
Thanks for sharing! I love it!
can you please tell me how can I get the selected value?
in the doc I have seen as I can user
multiselect-change
but not clear how to user it