Part 1 – The Front End

I’ve recently started using StimulusJS as the web framework for a Rails 5.2 application. My first task with it was to build a simple type-ahead search form. The StimulusJS documentation handbook jumps into loading HTML asynchronously. I had started to build the form in with a traditional JSON communication format, but the idea of instead getting to store all the HTML as Rails partials, build the HTML on the server so I didn’t have HTML embedded in Javascript files appealed to me, so I thought I’d try it.

The HTML Form

First I built the search form:
<div class="search-container" data-controller="searchform">
  <div class="search-form" >
    <h1>Search</h1>
    <form action="results" method="get">
      <label for="sys-search">Search the  System</label>
      <input name="search-term" type="search" tabindex="0"
             id="sys-search" minlength=2 placeholder="John Doe, McDonalds, 14 N Main St..."
             aria-label="Search through site content" accesskey="s"
             data-target="searchform.searchItem" autofocus
             data-action="input->searchform#requestSearch"
      >
    </form>
  </div>
  <div class="sys-search-results" data-target="searchform.searchResults">
    <ul>
      <li class="searching-interim">
        <span class="result-secondary">Searching....</span>
      </li>
    </ul>
  </div>
I quickly learned that while StimulusJS has some capability to replace kebab-case (sys-search) from HTML with snake_case in the controller (sys_search), I kept running into problems with keywords like ‘search-form’, so I gave up and replaced them without casing (see ‘searchform’ in the code above) and my bindings started working. You’ll see several stimulus tags in the HTML code:
  • data-controller: This tells Stimulus to link this area to the Javscript with the controller of the same filename.
  • data-target: Here we’re telling Stimulus: ‘Plan to get something from here’. In this case, I’m getting what the text for which the user is searching.
  • data-action: Finally, we tell Stimulus via ES6 function syntax the part with the arrow) to listen for a DOM event (In my case, the DOM event of ‘Input’). When an input event is detected, I want it to call the ‘requestSearch’ method in the searchForm controller.

The Javascript

On the Javascript side then I created my searchForm controller in /app/javascript/controllers/searchform_conttroller.js and gave it a requestSearch method:
import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "searchResults", "searchItem" ]

  connect() {
    console.log("connected to searchForm controller");
  }

  requestSearch(){
        const resultsArea = document.querySelector('.sys-search-results')
        if (this.searchItemTarget.value.length > 0){
            resultsArea.classList.add("showing")
            fetch('/search/query.html?search='+ this.searchItemTarget.value)
                .then(response=> {
                    return response.text();
                })
                .then(html=> {
                    resultsArea.innerHTML=html;
                });
        }else{
            resultsArea.classList.remove("showing")
        }

  }
}
You can see the Controller has two methods, connect() and requestSearch(). Since I’m not doing anything when the page loads, the Javascript is instantiated and then is tied to the DOM, my connect() method is nothing more than some console logging debug to give me some comfort the darn thing is even working. All the work here is done in requestSearch, which you’ll remember is fired every time the input element receives any information. I then do a couple of things:
  1. I set a constant called resultsArea tied to my DOM element where I want the search results to end up.
  2. When people first come to the page, the ‘sys-search-results’ div is hidden. Assuming there aren’t any results yet, I now show that results area underneath the input field. It already has a div in it that says ‘Searching…’, giving the user some clue the request is happening.
  3. I then perform a JSON ‘fetch’ to the server. In my case, I’m performing a GET request, so I’m adding the search term to the end of the URL after the question mark. This will send to the server something like https://<servername>/search/query.html?search=<search term>
  4. The first ‘then’ contains the response code (200, 404 etc). I could do something useful with this, like gracefully handle a bad request, but for this example I’m effectively just ignoring it and hoping it works.
  5. The second ‘then’ contains the data responded, which is what I care about. Since I’m getting the results back from the server as HTM, I can basically just paste it into the area over the ‘Searching…’ text.
Once you have StimulusJS installed and working in your Rails app, that’s pretty much all it takes to plug it into a page on the front-end. In a future article I’ll share how I handle the request and create the response on the Rails server.

Join the Conversation

2 Comments

  1. Hey! Thanks for sharing this post!

    I found that instead of querying the document with a query selector you could use the searchResults target to set its inner HTML, which allows multiple copies of this typeahead to exist in the same page and feels more “Stimulus” like:
    “`
    requestSearch(){
    const resultsArea = this.resultsAreaTarget;
    ….
    “`

Leave a comment

Your email address will not be published. Required fields are marked *