DEV Community

Cover image for A quality of life Stimulus.js controller
Eelco from Spinal for Spinal

Posted on • Updated on

A quality of life Stimulus.js controller

If you are developing any (Rails) app, you know how often have to log in to your own application. Enter your email and password for the umpteenth time isn't really 2023 is it? You could store them in your password manager, sure. But that also that is too much work. Besides you don't really want to clutter your password manager with dummy or seed data.

For the past few years, at least since Stimulus.js was introduced, I've been using this simple controller for all the SaaS products I developed.

Here it is, ready to be copied-and-pasted. Let's go over some of the more interesting bits afterwards.

app/javascript/controllers/development/fill_form_controller.js

import { Controller } from '@hotwired/stimulus'

export default class extends Controller {
  static targets = ["button"]

  connect() {
    if (this.#formElementsOnPage && this.#devEnvironment) {
      this.#addComponent()
    }
  }

  analysePage() {
    if (this.#formElementsOnPage == 1) {
      this.#fillForm()
    } else {
      this.buttonTarget.text = "Too many forms on this page…"
    }
  }

  analysePageWithKeybinding(event) {
    if (event.key === "f") {
      this.analysePage()
    }
  }

  // private

  get #formElementsOnPage() {
    return this.#numberOfFormElements > 0
  }

  get #devEnvironment() {
    return process.env.NODE_ENV === "development"
  }

  #addComponent() {
    this.element.insertAdjacentHTML("beforeend", this.#buttonTag())
  }

  get #numberOfFormElements() {
    return document.getElementsByTagName("form").length
  }

  get #buttonTag() {
    return `
      <button
        data-development--fill-form-target="button"
        data-action="development--fill-form#analysePage keydown@window->development--fill-form#analysePageWithKeybinding"
      >
        Fill form
      </button>
    `
  }

  #fillForm() {
    const forms = document.getElementsByTagName("form");

    [].forEach.call(forms, (form) => {
      this.#fillInputsFor(form)
    })
  }

  #fillInputsFor(form) {
    const inputs = form.getElementsByTagName("input");

    [].forEach.call(inputs, (input) => {
      let regex = /(^.*\[|\].*$)/g;
      let inputName = input.name.replace(regex, "")

      switch (inputName) {
        case "name":
          input.value = this.#randomiseValue(this.#nameValueOptions())
        break
        case "email":
          input.value = this.#randomiseValue(this.#emailValueOptions())
        break
        case "password":
          input.value = this.#randomiseValue(this.#passwordValueOptions())
        break
      }
    })
  }

  #nameValueOptions() {
    return [
      "Chris", "Kate", "Cameron"
    ]
  }

  #emailValueOptions() {
    return [
      "test@example.com"
    ]
  }

  #passwordValueOptions() {
    return [
      "password"
    ]
  }

  #randomiseValue(items) {
    return items[Math.floor(Math.random() * items.length)]
  }
}
Enter fullscreen mode Exit fullscreen mode

And then in app/view/layouts/application.html.erb

  <%= tag.div nil, data: { controller: "development--fill-form" } if Rails.env.development? %>
Enter fullscreen mode Exit fullscreen mode

You could also only add it to the layout for your sign up/log in instead. It's what I do.

How it works

All this class does is check if there's one form present on the page. If there's one (and not many), and the user presses f or the button in the bottom-right, the form fields for name, email and password gets, randomly, filled with any of the values from any of the _ValueOptions() functions.

Let's go over some of the more interesting bits.

connect() {
  if (this.#formElementsOnPage && this.#devEnvironment) {
    this.#addComponent()
  }
Enter fullscreen mode Exit fullscreen mode

If there are form elements on the page and if the environment equals "development", run this#addComponent(). The environment check is also done when adding the element on the page with Rails.env.development?. It really is just an extra safeguard.

Quick side note: the functions with # prefix are “private functions”. Similar to Ruby's private methods. I like to expose only the functions needed for the functionality to work. The // private comment does nothing really, I like it for a visual cue.

#buttonTag() {
return `
   <button
     data-development--fill-form-target="button"
      data-action="development--fill-form#analysePage keydown@window->development--fill-form#analysePageWithKeybinding"
      >
      Fill form
    </button>
  `
}
Enter fullscreen mode Exit fullscreen mode

This isn't a method I use often with Stimulus.js (which works best with HTML you have), but sometimes adding the HTML from the controller makes sense.

#fillInputsFor(form) {
  // …

[].forEach.call(inputs, (input) => {
    let regex = /(^.*\[|\].*$)/g;
    let inputName = input.name.replace(regex, "")

    switch (inputName) {
      // …
    }

  // …
}
Enter fullscreen mode Exit fullscreen mode

This function checks for the input's name value and inserts a random value from any of the _ValueOptions(). A tricky bit here is that Rails' expects input name's like model[name], so this is taken care off with some regex.

If you want to add more values to the current options, you can expand the arrays. Also if you need more fields, you can extend them easily.

Like this approach? Or do you know a better way? Do let me know.

Top comments (0)