New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linked Target Properties #61

Merged
merged 9 commits into from Jan 18, 2018

Conversation

Projects
None yet
3 participants
@javan
Copy link
Contributor

javan commented Jan 17, 2018

Define a controller's target names and Stimulus automatically creates properties for accessing them:

import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "source", "slide" ]

  initialize() {
    this.sourceTarget  // this.targets.find("source")
    this.sourceTargets // this.targets.findAll("source")

    this.slideTarget  // this.targets.find("slide")
    this.slideTargets // this.targets.findAll("slide")
  }
}

@javan javan added this to the 1.0 milestone Jan 17, 2018

@sstephenson

This comment has been minimized.

Copy link
Contributor

sstephenson commented Jan 17, 2018

If we pass the target name to the `this.targets.find()` method, Stimulus will return the first matching target element it finds. We can then read its `value` and use it to build our greeting string.
Let's try it out. Open `hello_controller.js` and update the `greet()` method like so:
```js
// src/controllers/hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
greet() {
const element = this.targets.find("name")
const name = element.value
console.log(`Hello, ${name}!`)
}
}
```

Let’s take this change all the way. No need to start by explaining this.targets—we can reserve that API for special cases. We should revise the whole section to introduce targets as properties right off the bat.

@javan

This comment has been minimized.

Copy link
Contributor

javan commented Jan 17, 2018

Good call. Made some revisions in af233fe

@sstephenson

This comment has been minimized.

Copy link
Contributor

sstephenson commented Jan 17, 2018

Much better! Probably need to revisit the title and intro, too:

## Targets Locate Important Elements By Name
We'll finish the exercise by changing our action to say hello to whatever name we've typed in the text field.
In order to do that, first we need a reference to the input element inside our controller. Then we can read the `value` property to get its contents.
Stimulus lets us mark important elements as _targets_ so we can easily reference them by name. Open `public/index.html` and add a magic `data-target` attribute to the input element:

“Locate” now seems to describe the implementation rather than the concept. And we aren’t really “referencing” elements “by name” anymore, either. I would prefer to describe it more along the lines of linking targets to controller properties.

@sstephenson

This comment has been minimized.

Copy link
Contributor

sstephenson commented Jan 17, 2018

I’m happy with chapter 2 of the handbook now. The last thing we should do is update the installation guide to mention the necessary Babel static properties plugin.

@javan

This comment has been minimized.

Copy link
Contributor

javan commented Jan 18, 2018

It turns out that the Babel plugin for class properties is either going to work great or not work at all with our implementation.

👍

Add the transform-class-properties plugin to your .babelrc file:

{
  "plugins": ["transform-class-properties"]
}

And classes with static properties like:

class A {
  static targets = […]
}

Are compiled to:

var A = function() {}
A.targets = […]

👎

Use the plugin's spec: true option like webpacker does:

{
  "plugins": ["transform-class-properties", { "spec": true }]
}

And the same class compiles to:

var A = function() {}
Object.defineProperty(A, "targets", { value: […] })

Thus bypassing our setter:

export class Controller {
readonly context: Context
static set targets(targetNames: string[]) {

In Babel 7, this is going to be the default behavior unless you use the loose: true option.

@dixpac

This comment has been minimized.

Copy link

dixpac commented Jan 18, 2018

Thinking out loud...is it possible to get target properties without specifying them explicitly inside the controller?

// src/controllers/hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  //static targets = [ "name" ]       without this line

  greet() {
    const element = this.nameTarget
    const name = element.value
    console.log(`Hello, ${name}!`)
  }
}
@javan

This comment has been minimized.

Copy link
Contributor

javan commented Jan 18, 2018

@dixpac we could with Proxy, but it's unsupported in IE and can't be polyfilled.

Avoid reliance on setter method for defining target properties
To avoid the potential pitfalls with Babel outlined in #61 (comment)
@javan

This comment has been minimized.

Copy link
Contributor

javan commented Jan 18, 2018

Possible solution to the Babel situation: c8e7696

@javan javan force-pushed the target-definitions branch from e3fc4df to 88bfde8 Jan 18, 2018

@javan javan merged commit 5bcae2f into 1-0 Jan 18, 2018

1 check passed

ci/circleci Your tests passed on CircleCI!
Details

@javan javan deleted the target-definitions branch Jan 18, 2018

@javan javan changed the title Target definitions Linked Target Properties Jan 29, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment