Syntax Error When Using "targets"

I believe I’ve integrated Stimulus with my Rails/TL app as I can print simple logs from connect methods. However, when trying to reference targets I get a syntax error.

window.application.register("calendar", class extends Stimulus.Controller {
  static targets = [ "href" ] // <-- Uncaught SyntaxError: Unexpected token =

  // ...
})

I’m not using webpacks, babel, or any other build system. I have stimulus.umd.js tucked away in my vendor directory. Here is my application.js file:

//= require jquery3
//= require rails-ujs
//= require popper
//= require turbolinks
//= require bootstrap-sprockets
//= require stimulus.umd

//= require_self
//= require_tree .

//= require trix

window.application = Stimulus.Application.start()

Any help would be appreciated - thanks!

Just an FYI, that same error message shows in the Glitch examples, but they do seem to work. I haven’t yet tried moving the examples outside of Glitch though.

This looks to be caused by a missing transformation. There is a closed issue on the Github when using babel. I’m not sure what the solution is when using the asset pipeline, but hopefully that helps.

Until the static class fields proposal is standardized, you’ll need the transform-class-properties plugin for Babel. See the Using Babel section of the installation guide for more details and an example configuration.

You can also use TypeScript, which has support for static class properties built-in.

I’m not sure what the solution is when using the asset pipeline

You can use a static get method instead of a static class property:

// app/assets/javascripts/controllers/calendar_controller.js
application.register("calendar", class extends Stimulus.Controller {
  static get targets() {
    return [ "href" ]
  }

  connect() {
    console.log(this.hrefTarget)
  }
})

Or, if you’re using CoffeeScript:

# app/assets/javascripts/controllers/calendar_controller.coffee
application.register "calendar", class extends Stimulus.Controller
  @targets = [ "href" ]

  connect: ->
    console.log(@hrefTarget)
1 Like

If I try something like this using TypeScript, I get something like a “property hrefTarget does not exist on type default” error in compile? How do you get Typescript to learn the dynamic target methods?

1 Like

The CoffeeScript solution works great, thanks @javan!

As mentioned in my original question, I’m not using Babel (or webpacks), just the plain ol’ Rails Asset Pipeline.

We’d love to figure out a way to define those types automatically. For now, you’ll have to declare the ones you’re using manually:

import { Controller } from "stimulus"

export default class extends Controller {
  nameTarget: Element
  nameTargets: Element[] 
  hasNameTarget: boolean

  static targets = [ "name" ]

  // …
}

You can also use TypeScript to compile JavaScript files without needing to declare any types in your source.

// tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "allowJs": true,
    "esModuleInterop": true
  }
}

Just for the heck of it, if you are using TypeScript with strictNullChecks: true then the declaration has to be something like this:

import { Controller } from "stimulus"

export default class extends Controller {
  nameTarget: Element | undefined

  static targets = [ "name" ]

  actionThing() { 
    this.nameTarget!.classList
  }
}

In other words, even though the nameTarget method will raise an exception rather than return undefined, TypeScript doesn’t know that, and forces you to both allow for the possibility when declaring the target and bypass it with !. when using the target.

I think, but I’m not sure, that this could be cleaned up by explicitly specifying the return values of the dynamically generated properties in target_properties.ts, rather then letting TypeScript infer them.

1 Like

I tried your way but I think it overwrites the property defined dynamically to undefined. Not sure how to make that work