@ramstack/alpinegear-bound
v1.2.4
Published
@ramstack/alpinegear-bound provides the 'x-bound' Alpine.js directive, which allows for two-way binding of input elements and their associated data properties.
Maintainers
Readme
@ramstack/alpinegear-bound
@ramstack/alpinegear-bound is a plugin for Alpine.js that provides the x-bound directive.
This directive allows for two-way binding between input elements and their associated data properties. It works similarly to the binding provided by Svelte and also supports synchronizing values between two Alpine.js data properties.
[!Note] This package is part of the
@ramstack/alpinegear-mainbundle. If you are using the main bundle, you don't need to install this package separately.
Installation
Using CDN
To include the CDN version of this plugin, add the following <script> tag before the core alpine.js file:
<!-- alpine.js plugin -->
<script src="https://cdn.jsdelivr.net/npm/@ramstack/alpinegear-bound@1/alpinegear-bound.min.js" defer></script>
<!-- alpine.js -->
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js" defer></script>Using NPM
Alternatively, you can install the plugin via npm:
npm install --save @ramstack/alpinegear-boundThen initialize it in your bundle:
import Alpine from "alpinejs";
import bound from "@ramstack/alpinegear-bound";
Alpine.plugin(bound);
Alpine.start();Usage
The syntax for binding properties is x-bound:property="dataprop".
Let's take the following example:
<div x-data="{ name: '' }">
<input x-bound:value="name" />
Hello <span x-text="name"></span>!
<button @click="name = 'John'">Change Name</button>
</div>🚀 Live demo | Alpine.js x-bound: Basic usage
In this example, we bind the name property to the value property of the <input> element. Since x-bound provides two-way binding, any changes to name will be reflected in the <input> element, as will occur when the button is clicked.
Shorthand Syntax
The x-bound directive also supports shorthand syntax: &.
So the previous example could be written as follows:
<input &value="name" />If the element's property name matches the bound data property, you can simplify this further:
<input x-bound:value />In this example, the repetition of the value in x-bound:value="value" is redundant, so we can simply shorten it to <input x-bound:value>. Since we can use & instead of x-bound, the example can be written as follows:
<input &value />More examples:
<div x-data="{ name: '', text: '', yes: true, methods: [] }">
<input &value="name" />
<textarea &value="text"></textarea>
<input &checked="yes" type="checkbox" />
<select &value="methods">
...
</select>
</div>🚀 Live demo | Alpine.js x-bound: Shorthand Syntax
Binding Numeric Inputs
For <input> elements with type="number" and type="range", values are automatically coerced into numbers. If the <input> value is empty or invalid, the bound property will be set to null.
<input &value="number" type="number" />🚀 Live demo | Alpine.js x-bound: Bind Numeric Inputs
Binding <input type="file">
For <input> elements with type="file", the binding is applied to the files property, resulting in a FileList object being assigned, containing the list of selected files.
<input &files type="file" accept="image/jpeg" />🚀 Live demo | Alpine.js x-bound: Bind Files
Binding <select>
To bind the value of a <select> element, use the value property:
<div x-data="{ fruit: '' }">
<select &value="fruit">
<option value="" disabled>Select...</option>
<option>Apple</option>
<option>Banana</option>
<option>Orange</option>
<option>Grape</option>
<option>Mango</option>
</select>
<p>
Fruit: <span x-text="fruit"></span>
</p>
</div>🚀 Live demo | Alpine.js x-bound: Binding select
For a <select multiple> element, the data property is an array containing the values of the selected options.
<div x-data="{ pets: ['goldfish', 'parrot'] }">
<select &value="pets" multiple>
<option value="cat">Cat</option>
<option value="goldfish">Goldfish</option>
<option value="parrot">Parrot</option>
<option value="spider">Spider</option>
</select>
Pets: <span x-text="pets"></span>
</div>🚀 Live demo | Alpine.js x-bound: Multiple select
Binding <details>
The directive also allows binding to the open property of <details> elements:
<div x-data="{ open: true }">
<details &open>
<summary>Details</summary>
<p>Something small enough to escape casual notice.</p>
</details>
<p>
<label>
<input &checked="open" type="checkbox" />
Open / Close
</label>
</p>
</div>🚀 Live demo | Alpine.js x-bound: Binding details
Binding <img> sizes
You can bind the naturalWidth and naturalHeight properties of an image after it loads:
<img src="..." &naturalWidth="width" &naturalHeight="height" />🚀 Live demo | Alpine.js x-bound: Binding image sizes
[!TIP] If you prefer using
kebab-casefor multi-word properties likenaturalWidth, you can write it asnatural-width. It will be automatically normalized internally:<img src="..." &natural-width="width" &natural-height="height" />
[!NOTE] As HTML attributes are case-insensitive, corresponding properties can be written as follows:
<img src="..." &naturalwidth="width" &naturalheight="height" />
[!NOTE] The
naturalWidthandnaturalHeightproperties are read-only and reflect the original image dimensions, available after the image has loaded.
Binding <video> sizes
You can bind the videoWidth and videoHeight properties of a video after it loads:
<video &videoWidth="width" &videoHeight="height">
<source src="..." type="video/mp4">
</video>🚀 Live demo | Alpine.js x-bound: Binding video sizes
Binding contenteditable elements
For contenteditable elements, you can bind the following properties:
<div &innerHtml="html" contenteditable="true"></div>🚀 Live demo | Alpine.js x-bound: Contenteditable bindings
Binding block-level element sizes
You can bind to the following properties to get the width and height of block-level elements,
measured with a ResizeObserver. The values will update whenever the element's size changes:
<div &client-width="width" &client-height="height"></div>🚀 Live demo | Alpine.js x-bound: Binding element dimensions
[!NOTE] These properties are read-only.
[!IMPORTANT] Elements with
display: inlinedon't have an explicit width or height (unless they are intrinsically sized, like<img>or<canvas>). Therefore, aResizeObservercannot track their size. If you need to observe their size, change theirdisplaystyle to something likeinline-block.Also keep in mind that CSS transforms do not trigger
ResizeObserverupdates.
Binding group of <input type="radio"> and <input type="checkbox">
The group of <input> elements that should function together can utilize the group bound property.
<div x-data="{ pets: ['goldfish', 'parrot'], contact: 'Email' }">
<!-- grouped checkboxes are similar to "select multiple"
and use an array for selected options -->
<input &group="pets" type="checkbox" value="cat" />
<input &group="pets" type="checkbox" value="goldfish" />
<input &group="pets" type="checkbox" value="parrot" />
<input &group="pets" type="checkbox" value="spider" />
<!-- grouped radio inputs are mutually exclusive -->
<input &group="contact" type="radio" value="Email" />
<input &group="contact" type="radio" value="Phone" />
<input &group="contact" type="radio" value="Mail" />
</div>🚀 Live demo | Alpine.js x-bound: Binding element dimensions
Binding input[type="checkbox"]:indeterminate property
The x-bound directive supports binding the indeterminate property of <input type="checkbox"> elements,
allowing you to control the checkbox's indeterminate state (a state where the checkbox is neither checked nor unchecked,
typically represented visually with a dash or similar indicator).
<div x-data="{ checked: false, indeterminate: true }">
<input type="checkbox" &checked &indeterminate />
<template x-match>
<span x-case="indeterminate">Waiting...</span>
<span x-case="checked">Checked</span>
<span x-default>Unchecked</span>
</template>
</div>🚀 Live demo | Alpine.js x-bound: Binding indeterminate
This is useful for scenarios like selecting a subset of items in a list, such as in a table header checkbox:
<table>
<thead>
<tr>
<th>
<input type="checkbox"
&indeterminate="isPartialSelected"
&checked="isAllSelected" />
</th>
<th>Value</th>
</tr>
</thead>
...
</table>🚀 Live demo | Alpine.js x-bound: Binding indeterminate (table)
In this example, the indeterminate property of the checkbox is bound to the isPartialSelected data property.
When isPartialSelected is true, the checkbox will be in the indeterminate state.
[!NOTE] The
indeterminatebinding is one-way. Changes to the indeterminate property in the DOM (e.g., via user interaction or JavaScript) do not update the bound data property.
Binding Alpine data properties
The directive also supports synchronizing values between two data properties.
<div x-data="{ number: 5 }">
<div x-data="{ count: 0 }" &count="number">
<button @click="count++">Increment</button>
</div>
Number: <span x-text="number"></span>
</div>🚀 Live demo | Alpine.js x-bound: Binding data properties
In this example, we bind the outer number property to the inner count property. Since number is initially set to 5, the count property is also set to 5 when the binding occurs.
By default, the binding is two-way, so changes in count are reflected in number and vice versa.
But what if we want changes to propagate in one direction only? For this case, the x-bound directive provides three modifiers to control data flow:
[!TIP]
inout: Binding works in both directions. This means that changes in one property are automatically reflected in the other and vice versa. This modifier is used by default.Example: If we have the property
&count="number", then changing the value ofcountwill also change the value ofnumber, and vice versa.
in: Binding works in one direction only — from the source property to the target property. This means that changes in the source property are passed to the target property, but changes in the target property do not affect the source property.Example: If we have
&count.in="number", then changes innumberwill be passed tocount, but changes incountwill not be reflected innumber.
out: Binding works in one direction only — from the target property to the source property. This means that changes in the target property are passed to the source property, but changes in the source property do not affect the target property.Example: If we have
&count.out="number", then changes incountwill be passed tonumber, but changes innumberwill not be reflected incount.
[!NOTE] The default behavior (
inout) can also be achieved using thex-modelandx-modelabledirectives.
Source code
You can find the source code for this plugin on GitHub:
https://github.com/rameel/ramstack.alpinegear.js/tree/main/src/plugins/bound
Related projects
@ramstack/alpinegear-main (README)
Provides a combined plugin that includes several useful directives.
This package aggregates multiple individual plugins, offering a convenient all-in-one bundle.
Included directives: x-bound, x-format, x-fragment, x-match, x-template, and x-when.
@ramstack/alpinegear-format (README)
Provides the x-format directive, which allows you to easily interpolate text using a template syntax similar to what's available in Vue.js.
@ramstack/alpinegear-template (README)
Provides the x-template directive, which allows you to define a template once anywhere in the DOM and reference it by its ID.
@ramstack/alpinegear-fragment (README)
Provides the x-fragment directive, which allows for fragment-like behavior similar to what's available in frameworks
like Vue.js or React, where multiple root elements can be grouped together.
@ramstack/alpinegear-match (README)
Provides the x-match directive, which functions similarly to the switch statement in many programming languages,
allowing you to conditionally render elements based on matching cases.
@ramstack/alpinegear-when (README)
Provides the x-when directive, which allows for conditional rendering of elements similar to x-if, but supports multiple root elements.
@ramstack/alpinegear-destroy (README)
Provides the x-destroy directive, which is the opposite of x-init and allows you to hook into the cleanup phase
of any element, running a callback when the element is removed from the DOM.
@ramstack/alpinegear-hotkey (README)
Provides the x-hotkey directive, which allows you to easily handle keyboard shortcuts within your Alpine.js components or application.
@ramstack/alpinegear-router (README)
Provides the x-router and x-route directives, which enable client-side navigation and routing functionality within your Alpine.js application.
Contributions
Bug reports and contributions are welcome.
License
This package is released as open source under the MIT License. See the LICENSE file for more details.
