0 books found

Explanation

This demo illustrates the power of combining Stimulus with StimulusReflex. It leverages Stimulus to debounce Reflex invocations (so we don't DOS the API) and provide feedback so users are aware of backend activity.

Code (61 LOC)

ERB (37 LOC)
app/views/book_searches/_demo.html.erb (37 LOC)
<div data-controller="book-search" data-reflex-root="#morph">
<input type="text" placeholder="Search for a book..." class="form-control form-control-lg d-inline-block col-4"
data-target="book-search.query" data-action="input->book-search#perform">
<div id="morph" class="mt-4">
<span data-target="book-search.activity" class="text-danger" hidden>
<i class="fas fa-spinner fa-spin"></i>
Searching for books...
</span>
<span data-target="book-search.count" class="text-success">
<strong><%= number_with_delimiter @books&.dig("num_found").to_i %></strong> books found
</span>
<table data-target="book-search.list" class="table mt-2" <%= "hidden" unless @books&.dig("docs").present? %>>
<thead>
<tr>
<th scope="col">Subject</th>
<th scope="col">Title</th>
<th scope="col">Author</th>
<th scope="col">Publish Date</th>
<th scope="col">ISBN</th>
</tr>
</thead>
<tbody>
<% if @books&.dig("docs").present? %>
<% @books["docs"][0,12].each do |book| %>
<tr>
<td><%= truncate book["subject"]&.join(", "), length: 30 %></th>
<td><%= truncate book["title"], length: 30 %></th>
<td><%= book["author_name"]&.join ", " %></td>
<td><%= book["first_publish_year"] %></td>
<td><%= book["isbn"]&.first %></td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
</div>
</div>
JavaScript (17 LOC)
app/javascript/controllers/book_search_controller.js (17 LOC)
import { debounce } from 'lodash-es'
import ApplicationController from './application_controller'
export default class extends ApplicationController {
static targets = ['query', 'activity', 'count', 'list']
connect () {
super.connect()
this.perform = debounce(this._perform, 350).bind(this)
}
beforePerform (element, reflex) {
this.activityTarget.hidden = false
this.countTarget.hidden = true
}
_perform (event) {
event.preventDefault()
this.stimulate('BookSearchReflex#perform', this.queryTarget.value)
}
}
Ruby (7 LOC)
app/reflexes/book_search_reflex.rb (7 LOC)
class BookSearchReflex < ApplicationReflex
def perform(query = "")
return unless query.size > 2
result = HTTP.get("http://openlibrary.org/search.json?#{{q: query}.to_query}")
@books = JSON.parse(result.to_s) if result.status == 200
end
end