Slim Select with StimulusJS
Goal: Use slim-select for searchable dropdowns and multiselect:
When a select dropdown has too many values, it becomes uncomfortable to pick a value.
You would need to add search functionality to the dropdown.
The easies solution might be HTML <datalist>
tag:
However this approach still allows plain-text input, requires input validation and styling.
A better approach would be to use a javascript library. Some years ago my go-to library would be “selectize-js”, however it reliest on jQuery. And in 2022 you don’t want jQuery as a dependency in your app!
So, now I would prefer to use tom-select or slim-select. Neither of these libaries relies on jQuery!
Prerequisites #
Boilerplate app where we will add slim-select:
# yarn add slim-select
rails g model user email
rails g scaffold payment amount:integer user:references
rails db:migrate
bundle add faker
rails c
10.times { User.create(email: Faker::Internet.email) }
Install slim-select #
# add the JS library with importmaps
./bin/importmap pin slim-select
# alternative - add the JS library with yarn
yarn add slim-select
# create stimulus controller that will initialize the JS
rails g stimulus slim
Initialize SlimSelect in the stimulus controller
// app/javascript/controllers/slim_controller.js
import { Controller } from "@hotwired/stimulus"
// add the JS
import SlimSelect from 'slim-select'
// add the CSS
import 'slim-select/dist/slimselect.css'
// import "slim-select/dist/slimselect.min.css";
// Connects to data-controller="slim-select"
export default class extends Controller {
static targets = ['field']
connect() {
new SlimSelect({
select: this.fieldTarget,
// closeOnSelect: false
})
}
}
Initialize the stimulus contorller on a rails field:
# app/views/posts/_form.html.erb
<%= form.select :user_id, User.pluck(:email, :id), {include_blank: true}, {data: { controller: 'slim', slim_target: 'field' } } %>
Result: dropdown select with search using slim-select:
Multiselect #
In this scenario, we will add multiple Tags
to a Post
.
Prerequisites:
rails g model tag name
rails g model post_tag post:references tag:references
10.times { Tag.create(name: Faker::Movie.title) }
# app/models/post.rb
has_many :post_tags
has_many :tags, through: :post_tags
# app/models/tag.rb
has_many :post_tags
has_many :posts, through: :post_tags
# app/models/post_tag.rb
belongs_to :post
belongs_to :tag
Whitelist params:
# app/controllers/posts_controller.rb
- params.require(:post).permit(:user_id)
+ params.require(:post).permit(:user_id, tag_ids: [])
Finally, just add the multiple: true
in the html options of the select field:
# app/views/posts/_form.html.erb
-<%= form.select :user_id, User.pluck(:email, :id), {include_blank: true}, {data: { controller: 'slim', slim_target: 'field' } } %>
+<%= form.select :tag_ids, Tag.all.pluck(:name, :id), {}, { multiple: true, data: { controller: 'slim', slim_target: 'field' } } %>
Result: dropdown multi-select with search using slim-select:
⚠️ Troubleshooting #
If the CSS for the multiselect does not render, you can try including it in a stylesheet:
<!-- app/views/layouts/application.html.erb -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/slim-select/1.27.1/slimselect.min.css" rel="stylesheet" crossorigin="" />
or
# app/views/layouts/application.html.erb
<%= stylesheet_link_tag "https://cdnjs.cloudflare.com/ajax/libs/slim-select/1.27.1/slimselect.min.css", "data-turbo-track": "reload" %>
or
/* app/assets/stylesheets/application.css */
@import url("https://cdnjs.cloudflare.com/ajax/libs/slim-select/1.27.1/slimselect.min.css");
You can also try manually running rails assets:precompile
, if you are not sure that the build happened 🤷
Better JS: without Stimulus target
#
You don’t really have to define the target
, since we are adding the controller on the same element!
connect() {
this.select = new SlimSelect({
select: this.element
})
}
disconnect() {
this.select.destroy()
}
-<%= form.select :user_id, User.pluck(:email, :id), {include_blank: true}, {data: { controller: 'slim', slim_target: 'field' } } %>
+<%= form.select :user_id, User.pluck(:email, :id), {include_blank: true}, {data: { controller: 'slim' } } %>
That’s it!
Resources:
Did you like this article? Did it save you some time?