Skip to content

Commit

Permalink
Document new features
Browse files Browse the repository at this point in the history
  • Loading branch information
dhh committed Sep 23, 2021
1 parent e97f3df commit 0943dff
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 4 deletions.
62 changes: 62 additions & 0 deletions docs/reference/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ The first argument to an action method is the DOM _event object_. You may want a
* to read the key code from a keyboard event
* to read the coordinates of a mouse event
* to read data from an input event
* to read params from the action submitter element
* to prevent the browser's default behavior for an event
* to find out which element dispatched an event before it bubbled up to this action

Expand All @@ -119,6 +120,7 @@ Event Property | Value
event.type | The name of the event (e.g. `"click"`)
event.target | The target that dispatched the event (i.e. the innermost element that was clicked)
event.currentTarget | The target on which the event listener is installed (either the element with the `data-action` attribute, or `document` or `window`)
event.params | The action params passed by the action submitter element

<br>The following event methods give you more control over how events are handled:

Expand Down Expand Up @@ -172,3 +174,63 @@ Instead, name your action methods based on what will happen when they're called:
```

This will help you reason about the behavior of a block of HTML without having to look at the controller source.

## Action Parameters

Actions can have parameters that are be passed from the submitter element. They follow the format of `data-[identifier]-[param-name]-param`. Parameters must be specified on the same element as the action they intend to be passed to is declared.

All parameters are automatically typecast to either a `Number`, `String`, `Object`, or `Boolean`, inferred by their value:

Data attribute | Param | Type
----------------------------------------------- | -------------------- | --------
`data-item-id-param="12345"` | `123` | Number
`data-item-url-param="/votes"` | `"/votes"` | String
`data-item-payload-param='{"value":"1234567"}'` | `{ value: 1234567 }` | Object
`data-item-active-param="true"'` | `true` | Boolean


<br>Consider this setup:

```html
<div data-controller="item spinner">
<button data-action="item#upvote spinner#start"
data-item-id-param="12345"
data-item-url-param="/votes"
data-item-payload-param='{"value":"1234567"}'
data-item-active-param="true">…</button>
</div>
```

It will call both `SpinnerController#start` and `ItemController#upvote`, but only the latter will have any parameters passed to it:

```js
// ItemController
upvote(event) {
// { id: 12345, url: "/votes", active: true, payload: { value: 1234567 } }
console.log(event.params)
}

// SpinnerController
start(event) {
// {}
console.log(event.params)
}
```

If we don't need anything else from the event, we can destruct the params:

```js
upvote({ params }) {
// { id: 12345, url: "/votes", active: true, payload: { value: 1234567 } }
console.log(params)
}
```

Or destruct only the params we need, in case multiple actions on the same controller share the same submitter element:

```js
upvote({ params: { id, url } }) {
console.log(id) // 12345
console.log(url) // "/votes"
}
```
56 changes: 53 additions & 3 deletions docs/reference/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default class extends Controller {
}
```

Controllers are instances of JavaScript classes that you define in your application. Each controller class inherits from the `Controller` base class exported by the `stimulus` module.
Controllers are instances of JavaScript classes that you define in your application. Each controller class inherits from the `Controller` base class exported by the `@hotwired/stimulus` module.

## Properties

Expand Down Expand Up @@ -123,9 +123,9 @@ In filenames, separate multiple words using either underscores or dashes (snake_

## Registration

If you use Stimulus with the `@stimulus/webpack-helpers` package, your application will automatically load and register controller classes following the conventions above.
If you use Stimulus for Rails with an import map or Webpack together with the `@hotwired/stimulus-webpack-helpers` package, your application will automatically load and register controller classes following the conventions above.

If you don't use the webpack helpers, your application must manually load and register each controller class.
If not, your application must manually load and register each controller class.

### Registering Controllers Manually

Expand All @@ -146,3 +146,53 @@ application.register("reference", class extends Controller {
//
})
```

### Preventing Registration Based On Environmental Factors

If you only want a controller registered and loaded if certain environmental factors are met – such a given user agent – you can overwrite the static `shouldLoad` method:

```js
class UnloadableController extends ApplicationController {
static get shouldLoad() {
return false
}
}

// This controller will not be loaded
application.register("unloadable", UnloadableController)
```

## Cross-Controller Coordination With Events

If you need controllers to communicate with each other, you should use events. The `Controller` class has a convenience method called `dispatch` that makes this easier. It takes an `eventName` as the first argument, which is then automatically prefixed with the name of the controller. The payload is held in `details`. It works like this:

```js
class ClipboardController extends Controller {
static targets = [ "source" ]

copy() {
this.dispatch("copy", details: { content: this.sourceTarget.value })
this.sourceTarget.select()
document.execCommand("copy")
}
}
```

And this event can then be routed to an action on another controller:

```html
<div data-controller="clipboard effects" data-action="clipboard:copy->effects#flash">
PIN: <input data-clipboard-target="source" type="text" value="1234" readonly>
<button data-action="clipboard#copy">Copy to Clipboard</button>
</div>
```

So when the `Clipboard#copy` action is invoked, the `Effects#flash` action will be too:

```js
class EffectsController extends Controller {
flash({ details: content }) {
console.log(content) // 1234
}
}
```
2 changes: 1 addition & 1 deletion docs/reference/lifecycle_callbacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ order: 01

# Lifecycle Callbacks

Special methods called _lifecycle callbacks_ allow you to respond whenever a controller connects to and disconnects from the document.
Special methods called _lifecycle callbacks_ allow you to respond whenever a controller or certain targets connects to and disconnects from the document.

<meta data-controller="callout" data-callout-text-value="connect()">

Expand Down
16 changes: 16 additions & 0 deletions docs/reference/values.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,22 @@ export default class extends Controller {

The two arguments can be named as you like. You could also use `urlValueChanged(current, old)`.

## Default Values

Values that have not been specified on the controller element can be set by defaults specified in the controller definition:

```js
export default class extends Controller {
static values = {
url: { type: String, default: '/bill' },
interval: { type: Number, default: 5 },
clicked: Boolean
}
}
```

When a default is used, the expanded form of `{ type, default }` is used. This form can be mixed with the regular form that does not use a default.

## Naming Conventions

Write value names as camelCase in JavaScript and kebab-case in HTML. For example, a value named `contentType` in the `loader` controller will have the associated data attribute `data-loader-content-type-value`.

0 comments on commit 0943dff

Please sign in to comment.