stimulus-value-bindings
Version:
One-way reactive DOM bindings for Stimulus JS
260 lines (186 loc) • 7.25 kB
Markdown
# stimulus-value-bindings 🪢
_Reactive DOM value bindings for [Stimulus JS](https://stimulus.hotwired.dev)._

[](https://github.com/allmarkedup/stimulus-value-bindings/actions/workflows/ci.yml)
## Overview
`stimulus-value-bindings` allows DOM element attribute values to be _reactively_ bound to Stimulus [controller values](https://stimulus.hotwired.dev/reference/values)
so that the DOM attributes are automatically updated when their bound value property changes.
Bindings are:
* **reactive** - every time a value is changed any bound attributes (or text contents) in the DOM are automatically (and transparently) updated to reflect the changes.
* **one-way** - the flow of updates is always from the controller to the DOM. Direct manipulation to bound attributes in the DOM will not result in the controller values being updated.
`stimulus-value-bindings` can help you drastically reduce the amount of boring _something-has-changed-and-now-the-DOM-needs-updating_ code in your Stimulus JS controllers.
> [!WARNING]
> This documentation is a work-in-progress! Please open an issue if you are having problems.
## Simple counter example
The example below is a simple 'counter' example that should help to demonstrate how reactive value bindings work.
▶️ [View this example running in JSBin](https://jsbin.com/hitotizesi/edit?html,output)
```js
// counter-controller.js
import { Controller } from "@hotwired/stimulus";
import { useValueBindings } from "stimulus-value-bindings";
export default class extends Controller {
static values = {
count: Number
}
connect(){
useValueBindings(this);
}
increment(){
this.countValue++;
}
decrement(){
this.countValue--;
}
}
```
```html
<div data-controller="counter">
<span id="count" data-counter-bind-text="countValue">0</span>
<button data-action="counter#increment">+</button>
<button data-action="counter#decrement">-</button>
</div>
```
When the `+` or `-` buttons are clicked the `span#count` element text content will be automatically be updated to reflect the current value of the `count` controller value. The counter display is kept in sync with the `count` value without needing to manually update the DOM after each change.
## Installation
Add the `stimulus-value-bindings` package to your `package.json`:
#### Using NPM:
```
npm i stimulus-value-bindings --save
```
#### Using Yarn:
```
yarn add stimulus-value-bindings
```
## Usage
The `stimulus-value-bindings` package exports a `useValueBinding` function that can be used to add reactive value binding functionality to controllers.
```js
import { Controller } from "@hotwired/stimulus";
import { useValueBindings } from "stimulus-value-bindings";
export default class extends Controller {
connect(){
useValueBindings(this);
}
}
```
Alternatively, the package also exports a 'ready to go' `ValueBindingsController` base controller if you prefer to extend rather than compose your classes:
```js
import { ValueBindingsController } from "stimulus-value-bindings";
export default class extends ValueBindingsController {
// ...
}
```
You can then delare [controller values](https://stimulus.hotwired.dev/reference/values) in the usual way, and bind DOM element attribute values and content to them using [binding data attributes](#binding-attributes).
```js
// read-more-controller.js
import { Controller } from "@hotwired/stimulus";
import { useValueBindings } from "stimulus-value-bindings";
export default class extends Controller {
static values = {
showMore: Boolean,
buttonText: String
}
connect(){
useValueBindings(this);
}
toggle(){
this.openValue = !this.openValue;
this.buttonTextValue = this.openValue ? "read less" : "read more";
}
}
```
```html
<div data-controller="read-more">
<p>This is the summary content.</p>
<button data-action="read-more#toggle" data-read-more-bind-text="buttonTextValue">read more</button>
<p data-read-more-bind-hidden="!showMore" data-read-more-bind-aria-expanded="showMore" hidden>
This is the additional content.
</p>
</div>
```
In the example above, clicking the `read more` button will toggle the `hidden` attribute on the 'additional content' `div` to hide or show it. The button text will additonally be updated to `read more` or `read less` according to whether the additional content is currently hidden or shown respectively.
## Adding bindings to elements
Bindings are declared on DOM elements using data attributes with the following format:
```
data-[identifier]-bind-[bindingType]="[valueName]"
```
* `[identifier]`: The [identifier](https://stimulus.hotwired.dev/reference/controllers#identifiers) of the target controller
* `[bindingType]`: See below for the types of bindings available.
* `[valueName]` The name of the [value getter property](https://stimulus.hotwired.dev/reference/values#properties-and-attributes) to bind to.
### Attribute bindings
The values of DOM element attributes can be bound to controller values using _attribute bindings_.
```
data-[identifier]-bind-[attribute-name]="[valueName]"
```
For example:
```js
// defined in example-controller.js
static values = {
progress: 0
}
```
```html
<div data-controller="example">
<h4>Uploading...</h4>
<progress data-example-bind-value="progressValue" max="100"></progress>
</div>
```
#### Boolean attributes
Boolean attributes will be added or removed according to the truthiness of the value they are bound to.
```js
// defined in example-controller.js
static values = {
hidden: Boolean,
}
```
```html
<div data-controller="example">
<div data-example-bind-hidden="hiddenValue">some content</div>
</div>
```
* When `hiddenValue` is `true`, the `hidden` attribute will be added to the bound element.
* When `hiddenValue` is `false`, the `hidden` attribute will be removed from the element.
### Element `textContent` binding
The `textContent` of elements can be bound to controller values using _text bindings_.
```
data-[identifier]-bind-text="[valueName]"
```
For example:
```js
// defined in example-controller.js
static values = {
count: Number
}
```
```html
<div data-controller="example">
<span data-example-bind-text="countValue">0</span>
</div>
```
### Element bindings
Elements can be bound to `Object`-type values. An attribute or content binding will be created for each of the object's properties.
```
data-[identifier]-bind="[valueName]"
```
For example:
```js
// defined in example-controller.js
static values = {
input: {
type: Object,
default: {
value: "default value",
disabled: true
}
}
}
```
```html
<div data-controller="example">
<input data-example-bind="inputValue">
<!-- renders: <input disabled="disabled" value="default value"> -->
</div>
```
## Credits
`stimulus-value-bindings` is inspired by (and borrows code from!) the [`x-bind`](https://alpinejs.dev/directives/bind) functionality in Alpine JS.
## License
`stimulus-value-bindings` is available as open source under the terms of the MIT License.