You can simplify the process by adding turbo_frame: "name"
to your form and submitting it, rather than manually setting the src
.
bin/rails g scaffold Item name
bin/rails db:migrate
bin/rails g stimulus search
# app/controllers/items_controller.rb
def index
@items = if params[:search].present?
Item.where(Item.arel_table[:name].matches("%#{params[:search]}%"))
else
Item.all
end
end
# app/views/items/index.html.erb
<%= tag.div data: {controller: "search"} do %>
<%= form_with url: :items, method: :get,
data: {search_target: "form", turbo_frame: :items} do |f| %>
<%# NOTE: target the frame ^^^^^^^^^^^^^^^^^^^ %>
<%= f.search_field :search, placeholder: "search...",
data: {action: "search#autoSearch"} %>
<% end %>
<%= turbo_frame_tag :items, target: :_top,
data: {search_target: "results", turbo_action: :advance} do %>
<%# NOTE: to update url as well ^^^^^^^^^^^^^^^^^^^^^^ %>
<%= render @items %>
<% end %>
<% end %>
// app/javascript/controllers/search_controller.js
import { Controller } from "@hotwired/stimulus";
// Connects to data-controller="search"
export default class extends Controller {
static targets = ["form", "results", "item"];
connect() {
this.resultsTarget.addEventListener("turbo:frame-render", this.resultsHandler);
}
resultsHandler(event) {
event.target.querySelectorAll("li").forEach((li) => {
li.setAttribute("loaded", true); // <li loaded="true"></li>
});
}
autoSearch() {
// don't search on every keystroke
clearTimeout(this.timeout);
this.timeout = setTimeout(() => this.formTarget.requestSubmit(), 500);
}
// NOTE: you can also add a target and use target callbacks
// <li data-search-target="item"> </li>
itemTargetConnected(element) {
// to skip initial page load
if (this.resultsTarget.src) {
element.setAttribute("loaded", true);
}
}
disconnect() {
this.resultsTarget.removeEventListener("turbo:frame-render", this.resultsHandler);
}
}
To streamline the process further, consider using data-action
instead of adding event listeners within the controller:
<%= turbo_frame_tag :items, target: :_top,
data: {
turbo_action: :advance,
action: "turbo:frame-render->search#resultsHandler"
} do %>
<%= render @items %>
<% end %>