# Terminal
bin/importmap pin tom-select
yarn add tom-select
bin/rails g stimulus select
bin/rails g stimulus select_with_dependent
rails g controller searches/products
# config/importmap.rb
pin "tom-select", to: "https://cdn.jsdelivr.net/npm/tom-select@2.4.3/+esm"
# app/views/layouts/application.html.erb
<link href="https://cdn.jsdelivr.net/npm/tom-select@2.4.3/dist/css/tom-select.css" rel="stylesheet">
# app/javascript/controllers/select_controller.js
import { Controller } from "@hotwired/stimulus"
import TomSelect from "tom-select"
// Connects to data-controller="select"
export default class extends Controller {
connect() {
new TomSelect(this.element)
}
disconnect() {
if (this.element.tomselect) {
this.element.tomselect.destroy()
}
}
}
# app/views/welcome/index.html.erb
<div>
<h1 class="font-bold text-4xl">Welcome#index</h1>
<p>Find me in app/views/welcome/index.html.erb</p>
<%= form_with url: root_path do |form| %>
<%= form.label :company, "Search companies", class: "font-bold" %>
<%= form.select :company, Company.all.collect { |c| [c.name, c.id] },
{ prompt: "Select a company" },
"data-controller": "select-with-dependent",
"data-action": "change->select-with-dependent#update",
"data-select-with-dependent-url-value": searches_products_path,
"data-select-with-dependent-target-value": "product_select",
"data-select-with-dependent-param-value": :company_id,
class: "mb-3" %>
<%= form.label :product, "Search products", class: "font-bold" %>
<%= form.select :product, Product.all.collect { |c| [c.name, c.id] },
{ prompt: "Select a product" },
"data-controller": "select",
id: :product_select,
class: "mb-3" %>
<%= form.submit "Search", class: "bg-blue-500 text-white p-2 rounded" %>
<% end %>
</div>
# config/routes.rb
namespace :searches do
resources :products, only: :index
end
# app/controllers/searches/products_controller.rb
module Searches
class ProductsController < ApplicationController
def index
@products = Product.where(filter_by_company)
render json: @products.to_json
end
private
def filter_by_company
return {} unless params[:company_id].present?
{ company_id: params[:company_id] }
end
end
end
# app/javascript/controllers/select_with_dependent_controller.js
import { Controller } from "@hotwired/stimulus"
import TomSelect from "tom-select"
// Connects to data-controller="select-with-dependent"
export default class extends Controller {
static values = {
url: String,
param: "id",
target: String,
target_name: "name",
target_value: "id"
}
connect() {
new TomSelect(this.element)
}
disconnect() {
if (this.element.tomselect) {
this.element.tomselect.destroy()
}
}
update() {
const url = new URL(this.urlValue, window.location.origin)
url.searchParams.set(this.paramValue, this.element.value)
fetch(url, {
headers: { Accept: "application/json" }
})
.then(response => response.json())
.then(data => {
const target = document.getElementById(this.targetValue)
if (target) {
const valueKey = this.targetValueValue
const nameKey = this.targetNameValue
if (target.tomselect) {
target.tomselect.clearOptions()
data.forEach(option => {
target.tomselect.addOption({ value: option[valueKey], text: option[nameKey] })
})
target.tomselect.refreshOptions(false)
} else {
target.innerHTML = ""
data.forEach(option => {
const opt = document.createElement("option")
opt.value = option[valueKey]
opt.textContent = option[nameKey]
target.appendChild(opt)
})
}
}
})
}
}