Stimulus Discourse

Using Stimulus Class and Values API without a Build Step

I’m attempting to use the new values and classes API but not sure how to do that now while setting the type on the value. It has been working fine without a build step before but I seem to have the new syntax wrong:

static get values() {
              return [ "email: String" ]
            }

So how do you go about using Stimulus without Webpack now?

The return value should be an object, not an array:

static get values() {
  return { email: String }
}

Ah, good catch! Here’s why I’m really trying to do with an array of email addresses but I keep getting an undefined error in the console:

    (() => {
      const application = Stimulus.Application.start()

      application.register("mycontroller", class extends Stimulus.Controller {
        static get targets() {
          return [ "email" ]
        }

        static get values() {
          return { '[email1:"test1@domain.com", email2:"test2@domain.com", email3:"test3@domain.com"]': Object }
        }

        emailValueChanged() {
          console.log( this.emailValue )
}

})
    })()

And on the input I’m looping over:

<input data-action="mycontroller#emailValueChanged" type="text" data-mycontroller-target="email">

I keep getting an undefined error in the console. Here is the complete script I tried:

   <script>
    (() => {
      const application = Stimulus.Application.start()

      application.register("mycontroller", class extends Stimulus.Controller {
        static get targets() {
          return [ "email" ]
        }

        static get values() {
          return { '["test@domain.com", "test2@domain.com", "test3@domain.com"]': Object }
        }

        emailValueChanged() {
          console.log( this.emailValue )
        }

   
    
  })
})()

And on the input:

<input data-action="mycontroller#emailValueChanged" type="text" data-mycontroller-target="email">

Hey @dbarnes!

Are you trying to fetch the email from the <input> tag here or is that tag incidental and you’re trying to do something else with the email values?

The Values API is just a veneer over the Data Map API that simplifies the syntax and automatically handles type conversion.

Happy to help out, but I want to make sure I’m helping to solve the correct problem :slight_smile: Can you clarify what your end goal is here?

Thanks!

Yes I’m trying to fetch the email from the input, display it somewhere, and then eventually send it to the server. It doesn’t have to be an input though. I’m just trying to figure out how to take some json data from the server, let the user update it, then post it back to the server. Any help would be greatly appreciated!

Gotcha. The Data Map API (and thus the Values API) is for dealing with data attributes, so not quite what you want here.

In this case, you’d just want to define the input as a target (which you have) and then you can use Stimulus Targets to easily access that DOM element and then work with it using vanilla JS. For instance:

class extends Stimulus.Controller {
  static get targets() {
    return ["email"]
  }

  emailValueChanged() {
    const email = this.emailTarget.value  // <-- This retrieves the value from the input field
    // ...
  }
}

And the HTML might look like:

<div data-controller="mycontroller">
  <input type="text" data-mycontroller-target="email" data-action="mycontroller#emailValueChanged"></input>
</div>

And that’ll detect the change in the form and let you access it. Now, because Stimulus is really meant to work with the HTML you already have and not as a template renderer for API requests, the preferred way to get user data and allow them to update it would be to simply have the backend render an HTML form that contains the user data and then just do a standard form submission (e.g. via an <input type="submit">) to send it back. If need be, you can access the form as a target and call its submit() method, instead.

Hope this helps!

Ok I see.Thanks for the clear explanation. So what if I built a layout using data attributes and wanted to give the user the ability to make changes to it by changing the number of columns from 1 to 2:

<div data-columns=“1”>
<article>My content</article>
<aside>sidebar</aside>
</div>

Is that what the new values and classes API is for? I guess I’m trying to figure out in general how to use the new API to get data using stimulus, allow a user to alter it on the client side, and post it back to the server. The input tag really was just incidental. This is my first time using Stimulus and I haven’t used the Data Map API yet so I figured it would be best to fully understand the new API now and what problems it’s trying to solve. Something’s not fully clicking for me and documentation and online tutorials are very limited. I would say this is the weakest point of Stimulus.

That’s what the Data Map API is for - so by extension, the Values and Classes APIs are as well. But it’s worth getting to know and understand the Data Map API to really appreciate what Values and Classes add on top of that. The Data Map API isn’t going anywhere, and in simple cases will be less verbose to use. And Data Maps are just sugar on top of native HTML data attributes.

I’ll start by demonstrating a component that is built to fetch data from a URL that’s passed in via a data property, rather than being hardcoded. Thus, different instances of the component can fetch data from different URLs.

<div data-controller="fetch" data-fetch-url="/some-url"></div>

Using Data Maps:

export default class extends Controller {
  connect() {
    fetchContent()
  }

  fetchContent() {
    const url = this.data.get('url') // <-- Retrieve the data value
    // ...
  }
}

Using Values API:

export default class extends Controller {
  static values = {
    url: String
  }

  connect() {
    fetchContent()
  }

  fetchContent() {
    const url = this.urlValue // <-- Retrieve the data value
    // ...
  }
}

So in this case, the Values API is a little more code altogether, but saves a few characters in the one place that we call it. Not much to be gained in this specific example over just using Data Maps.

For now, I’d recommend starting with data maps. As you use it more and get comfortable with it, you’ll likely be able to come back and read through the Values docs and the times where it’s advantageous will become more evident. Values really are just a convenience layer over a convenience layer (Values -> Data Maps -> Data Attributes), so it’s going to be beneficial to understand data attributes and data maps first.

In the columns example, using data maps would look something like this:

<div data-controller="columns" data-columns-num="1" data-columns-url="/userPrefs">
  <div>
    <button data-action="columns#increase">+</button>
    <button data-action="columns#decrease">-</button>
  </div>

  <article>My content</article>
  <aside>sidebar</aside>
</div>
export default class extends Controller {
  increase() {
    this.data.set('num') = this.data.set('num')

    const formData = new FormData()
    formData.append('columns', this.data.get('num'))

    fetch(this.data.get('url'), {
        method: 'PUT',
        body: formData
      }
    )
  }

  decrease() {
    // ...
  }

So in this example, the changes are being recorded to a /userPrefs URL that is passed into the controller via a data property on the controller, and the column count is being defined as a piece of data, as well. Clicking on a button grabs the column count from the data property, increases it by one, and sends that back to the server.

Note, this is not robust code - for instance, you’d want to handle a response from the server, you’d want to make sure the “num” property is an integer, etc. But it illustrates a simple example of how to grab the data, update it, and send it to the server. Also note that the sending of the data is just using vanilla JS - Stimulus just gets out of your way for most stuff and lets Javascript be the star of the show.

Hope this helps!