A few sneak peeks into Hey.com technology (V - Stimulus enhancements)
Tags: rails, ruby, webdev, javascript
Published: 2020-06-23 20:03:44

> **Update**: as of Dec 4th, 2020, **Stimulus 2.0 has been [released](https://github.com/stimulusjs/stimulus/releases/tag/v2.0.0)** and this article describes the main features of the new version.

Today, I’ll show you some nice little syntactic sugar that should be released with the next Stimulus version − **the new `values` and `classes` APIs**. This may not be entirely news for those who watch the [Stimulus forum](https://discourse.stimulusjs.org/t/coming-in-stimulus-1-2-the-classes-and-values-apis/505), but I still think this stuff is very well worth mentioning as it makes the Stimulus controllers more concise and flexible.

Both new APIs originate from this great [pull request by Sam Stephenson](https://github.com/stimulusjs/stimulus/pull/202) and both of them are also used on the Hey website so I really expect them to be included in the new Stimulus release, soon!

Let’s have a look at what goodness they bring. But before that we should talk a bit about the **`data` API** that has been with Stimulus since long ago.

### A small refresh on Stimulus data API

In case you need to pass some data to your Stimulus controller, you normally do that via the [data API](https://stimulusjs.org/reference/data-maps).

Suppose we want to build an online currency conversion page that will accept an amount in US dollars and convert it to Euros. I created a very basic version of such component in a [JS fiddle](https://jsfiddle.net/apwgoec5/15/), feel free to have a look and play with it…

> Note that the `targets` definition in the fiddle has to be slightly different from the syntax you’ll normally write if you use the webpack bundler (this is explained [here](https://stimulusjs.org/handbook/installing#using-without-a-build-system)).
> I’m also aware that `Float` is not a very good data type when working with money but let’s ignore such details for now 😏.

The Stimulus data API is used here for passing the conversion rate to the controller − it’s the `data-conversion-rate` attribute:

```html
<div data-controller="conversion" data-conversion-rate="0.881709">
  ...
</div>
```

The Stimulus controller then gets the data value via the **data API `get` method**:

```javascript
// conversion_controller.js
export default class extends ApplicationController {
  ...

  calculate() {
    ...
    const convertedValue = parseFloat(this.data.get("rate")) * parseFloat(value)
    ...
  }
}
```

There are other methods for working with such data besides `get`, similar to the ruby hash syntax a bit − see the [docs](https://stimulusjs.org/reference/data-maps) for more info on that.

Now, the data API has a few limitations:

- It **only works with Strings**. That’s why we had to explicitly parse the data value into `Float` using `parseFloat()`. You cannot work with numbers, arrays, objects,... only strings in the data API.

- If we wanted to add more currencies to the converter, we would have to either:
  - add more separate data attributes to the HTML element, one for each currency (and the controller would have to “know” about them), or
  - we would have to construct our own custom serialization technique to “encode” all currency rates into a single data attribute (and, again, the controller would have to “know” about this technique).  

    Both options seem quite cumbersome and that’s, I think, exactly why the `values` API was born.

### The `values` API

I created [another JS fiddle](https://jsfiddle.net/ohuq95r6/27/) that shows the same converter, only a bit extended in its capabilities, but **using the new APIs** (play with it!). 

The `values` API has several distinct new features and syntax enhancements:

- It works not only with Strings but **also with Numbers (Floats), Booleans, Arrays and even Objects (Hashes)**. Stimulus also brings in a convention to serialize all these types to and from the HTML attributes so you don’t have to bother with that ([details in the pull request](https://github.com/stimulusjs/stimulus/pull/202)).

    You can clearly see a non-String type in the **`values` definition** in the controller. So, our `rates` value is an Object, not String (again, the syntax has to be a bit different in the [fiddle](https://jsfiddle.net/ohuq95r6/27/) but it is equivalent to this):

  ```javascript
static values = {
  rates: Object
}
  ``` 

- Stimulus **defaults to blank / empty / zero values** when the corresponding data attributes are missing in the HTML element.

- The API provides a **new convention for naming** the HTML attributes and accessing the values in controllers: e.g., for a `data-{controller-name}-{value-name}-value` attribute, you can get it’s value using `this.{valueName}Value` controller method. In our example, as we said, we store the multiple currency rates as an Object in the value attribute and we access it like the following in the controller:

```html
We define the value object like this in HTML:

<div data-controller="conversion" 
     data-conversion-rates-value="{&quot;EUR&quot;: 0.881709, &quot;GBP&quot;: 0.798940, &quot;AUS&quot;: 1.435456, &quot;IND&quot;: 75.499926}">
...
</div>
```

```javascript
// The value object is accessed like this in the controller:
console.log(this.ratesValue)

// outputs:
// {
//   AUS: 1.435456,
//   EUR: 0.881709,
//   GBP: 0.79894,
//   IND: 75.499926
// }
```

- Note that the conversion rates is a hash (an object) of Floats, not Strings − **Stimulus did the de-serialization and type conversion for us**.

- Also note that **we must properly [HTML-encode](https://mothereff.in/html-entities#%7B%22EUR%22%3A%200.881709%2C%20%22GBP%22%3A%200.798940%2C%20%22AUS%22%3A%201.435456%2C%20%22IND%22%3A%2075.499926%7D) the value in the attribute** but this is what most frameworks (such as Rails) usually do automatically. It’s a bit of shame that this makes the HTML source much less readable but luckily modern browser dev tools display these encoded strings nicely:

![Encoded value in dev tools](https://dev-to-uploads.s3.amazonaws.com/i/qct7qk15jdgzmtomiw58.png)

We see that once we have the conversion rates available in a single object, we can quite easily enumerate and show them in our sample converter. It is also trivial to add a new currency, now. This is all possible thanks to the new values API!

There are a few other niceties in the values API planned, see the [pull request](https://github.com/stimulusjs/stimulus/pull/202) for details, if you’re too curious.

### The `classes` API

Another common task in Stimulus controllers, is updating the CSS classes of the corresponding elements and their various children elements. New Stimulus should provide a new API for handling this, as is also shown in the [JS fiddle](https://jsfiddle.net/ohuq95r6/27/).

- Similar to `targets` or `values`, in the Stimulus controller you **define the `classes`** that you expect to be set in the HTML element.

- There is again a consistent convention on naming the class attributes in the HTML element, as well as the methods for accessing them in the JS controller. Just try to fill in a price higher than $1000 in the converter and see what happens…

> One thing that I personally dislike about the new classes API is that **it does not support atomic styling** (you know what I’m talking about if you used the [Tailwind](https://tailwindcss.com/) or [Tachyons](tachyons.io/) CSS frameworks). 
> I really like atomic styling but adding more than a single class to the Stimulus `class` attribute leads to a nasty exception! There is a [proposal](https://github.com/stimulusjs/stimulus/pull/202#issuecomment-620553981) in the pull request to address this issue but I couldn’t find any attempt to use this in Hey code… 😔

### More consistent naming convention

The new API adds two proposals for naming the HTML attributes for classes and values − they should, not surprisingly, end with `"...-value"` or `"...-class"`. Nice, but now it turns out that the current convention for naming `targets` (a feature of Stimulus that we did not talk about today, see the [docs](https://stimulusjs.org/reference/targets)) goes against it. 

That’s why new Stimulus will encourage you to rewrite your target attribute names from `data-target="{controller-name}.{target-name}"` to `data-{controller-name}-target="{target-name}"` and the old syntax is going to be slowly deprecated. In the [JS fiddle](https://jsfiddle.net/ohuq95r6/27/), you’ll see the new syntax in the two `target` HTML attributes (`input` and `converted`).

### Conclusion and a note about data API

So, this is what we can expect in the new Stimulus release! A more convenient API for passing values between your HTML and JS controllers and a nice little syntactic sugar for handling CSS classes.

How are these new APIs used in Hey? Let’s just show a few examples, without much further commentary.

In the `search_controller.js` (which manages… emmm… the main site search!), you’ll find the following `values` and `classes` definitions:

![search_controller.js](https://dev-to-uploads.s3.amazonaws.com/i/rgrls4is6iwsuwz21dig.png)

This is e.g. how one of the values is actually used in the controller:

![Value usage in the search controller](https://dev-to-uploads.s3.amazonaws.com/i/76pay90t7k9m1019mh81.png)

And this is what the corresponding HTML element looks like. Marked are the **value attributes** (red) and **class attributes** (green) in the element:

![Main search element](https://dev-to-uploads.s3.amazonaws.com/i/yexwlzno486abahoc1nu.png)

And you know what is really interesting? **The current Stimulus data API seems not to be used in Hey at all!** Why is that? As is also noted in the pull request, the new values API may actually serve as a better successor for the data API − the values API can do everything the same and more… and the syntax is more consistent with other features. Thus, after the new Stimulus release there will be no big reason to continue using the data API. **The data API is dead, long live the values API!**

_Next time, we’ll get back to analyzing live Hey pages updates, this time via the `<template>` element. There’s some exciting stuff, there, stay tuned!_ 😉