Skip to content

Blogging On Rails

Everything on Rails!

HOTWire: Where do I store my HTML state?

We’re used to storing all of our data in the database, and letting Active Record pull it out, Action View to format it, and Action Controller to manage the request and response. But when we want quick client side interactivity, sometimes we need some extra data annotations on the HTML side that we can use without needing to communicate with the server. Most of this data is used by Stimulus, but Turbo has a few tags that can be useful.

<head> State

Other Javascript frameworks, like Vue.js, React, and Angular, typically generate HTML on the client side, in Javascript. We can use the fact we’re sending server generated HTML Over The Wire to store information we might need in the '<head> tag of the page. Turbo will keep these values in sync as each page loads. Rails also has a provides helper that can be used insert a tag into the head of the page. For example, if an account-id should be in the head, you can use this anywhere in a view:

<% provide :head, tag.meta(name: "account-id", content: "1") %>

If your layout, like application.html.erb has this line:

<%= yield :head %>

The account id meta tag will look like:

<meta name="account-id" content="1">

Now, in a Stimulus controller, you could add a function like:

function getMetaValue(name) {
  const element = document.head.querySelector(`meta[name="${name}"]`);
  return element.getAttribute("content");
}

This will help read that value from the head, and you can include it wherever you want:

let accountId =  getMetaValue("account-id")

Controller State

The Stimulus controller is a Javascript object, so properties can be set on the editor. You may have be using a third party library to create an object and keep it around during the controller’s life. For example, if you’re using Tributeto add mentions in a textfield, you can use this and store it to a variable:

connect() {
  this.tribute = new Tribute({
    collection: []
  });
}

And in a different method, you can access the tribute variable:

addNames() {
  this.tribute.appendCurrent([
    { name: "Howard Johnson", occupation: "Panda Wrangler", age: 27 },
    { name: "Fluffy Croutons", occupation: "Crouton Fluffer", age: 32 }
  ]);
}

Data Attributes

Stimulus provides two mechanisms to make data available for your controller.

Values properties

The first is the values properties. These allow you to set parameters for the controller. For example, if you had a toggle like you would find in iOS, you may have an enabled attribute on it.

Button Toggled Off
Button Toggled On

If the controller is named toggle, then adding a data attribute enabled looks like this:

 <button type="button" 
    data-controller="toggle" 
    data-toggle-enabled-value="true" 
...

In the controller, you add the values target, and can access it anywhere in the controller:

// Connects to data-controller="toggle"
export default class extends Controller {
  static values = { enabled: Boolean };

  connect() {
    if (this.enabledValue) {
      console.log("This is enabled");
    }
  }
}

Classes properties

Since changing classes is a common interactive enhancement, controllers often need to toggle classes. The classes properties assumes the values are strings, and will split apart multiple classes if they’re separated by a space. The button toggle example above needs four different classes to change, for the background color and the movement of the circle. Add these classes in the HTML:

    data-toggle-enabled-class="bg-blue-600"
    data-toggle-disabled-class="bg-gray-200"
    data-toggle-enabled-translate-class="translate-x-5"
    data-toggle-disabled-translate-class="translate-x-0"

And then they’re accessible in the controller:

  static classes = [
    "enabled",
    "enabledTranslate",
    "disabled",
    "disabledTranslate",
  ];

They can be used in a method when you want to swap out classes:

  setEnabled() {
    this.frameTarget.classList.add(this.enabledClass);
    this.frameTarget.classList.remove(this.disabledClass);
    this.circleTarget.classList.add(this.enabledTranslateClass);
    this.circleTarget.classList.remove(this.disabledTranslateClass);
  }

Action Params

Actions can also pass along values as parameters to the method that’s called. The toggle button has a switch method that’s called when button is clicked. Adding an attribute, such as an id, could become available to the switch method:

data-action="toggle#switch"
data-toggle-id-param="123"

When the action fires, the params are added to the event:

 switch(event) {
    console.log(event.params.id);
  }

Data everywhere

There are lots of options for passing data along from the server to the front end, and making it available for our Stimulus controllers. Our website can be interactive without the need for dipping into a heavier Javascript framework.

Make Interactivity Default 

Make your web app interactive now with easy to implement and simple to add HOTWire integrations. 

Enter your email and get a free sample of my HOTWire Tutorials ebook.

We won’t send you spam. Unsubscribe at any time.

Leave a Reply

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