Skip to content

Commit

Permalink
Allow x-model to be accessed programmatically (#2303)
Browse files Browse the repository at this point in the history
  • Loading branch information
calebporzio committed Nov 2, 2021
1 parent 286ac99 commit 98805c3
Show file tree
Hide file tree
Showing 18 changed files with 83 additions and 18 deletions.
4 changes: 1 addition & 3 deletions packages/alpinejs/src/directives/x-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ directive('data', skipDuringClone((el, { expression }, { cleanup }) => {

let data = evaluate(el, expression, { scope: dataProviderContext })

if( data === undefined ) {
data = {}
}
if (data === undefined) data = {}

injectMagics(data, el)

Expand Down
13 changes: 13 additions & 0 deletions packages/alpinejs/src/directives/x-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ directive('model', (el, { modifiers, expression }, { effect, cleanup }) => {

cleanup(() => removeListener())

// Allow programmatic overiding of x-model.
let evaluateSetModel = evaluateLater(el, `${expression} = __placeholder`)
el._x_model = {
get() {
let result
evaluate(value => result = value)
return result
},
set(value) {
evaluateSetModel(() => {}, { scope: { '__placeholder': value }})
},
}

el._x_forceModelUpdate = () => {
evaluate(value => {
// If nested model key is undefined, set the default value to empty string.
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/bind.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 4
title: bind
---

# `x-bind`
# x-bind

`x-bind` allows you to set HTML attributes on elements based on the result of JavaScript expressions.

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/cloak.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 12
title: cloak
---

# `x-cloak`
# x-cloak

Sometimes, when you're using AlpineJS for a part of your template, there is a "blip" where you might see your uninitialized template after the page loads, but before Alpine loads.

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/data.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 1
title: data
---

# `x-data`
# x-data

Everything in Alpine starts with the `x-data` directive.

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/effect.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 11
title: effect
---

# `x-effect`
# x-effect

`x-effect` is a useful directive for re-evaluating an expression when one of its dependencies change. You can think of it as a watcher where you don't have to specify what property to watch, it will watch all properties used within it.

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/for.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 8
title: for
---

# `x-for`
# x-for

Alpine's `x-for` directive allows you to create DOM elements by iterating through a list. Here's a simple example of using it to create a list of colors based on an array.

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/html.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 7
title: html
---

# `x-html`
# x-html

`x-html` sets the "innerHTML" property of an element to the result of a given expression.

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/if.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 16
title: if
---

# `x-if`
# x-if

`x-if` is used for toggling elements on the page, similarly to `x-show`, however it completely adds and removes the element it's applied to rather than just changing its CSS display property to "none".

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/ignore.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 11
title: ignore
---

# `x-ignore`
# x-ignore

By default, Alpine will crawl and initialize the entire DOM tree of an element containing `x-init` or `x-data`.

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 2
title: init
---

# `x-init`
# x-init

The `x-init` directive allows you to hook into the initialization phase of any element in Alpine.

Expand Down
38 changes: 37 additions & 1 deletion packages/docs/src/en/directives/model.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 7
title: model
---

# `x-model`
# x-model

`x-model` allows you to bind the value of an input element to Alpine data.

Expand Down Expand Up @@ -336,3 +336,39 @@ The default throttle interval is 250 milliseconds, you can easily customize this
```alpine
<input type="text" x-model.throttle.500ms="search">
```

<a name="programmatic access"></a>
## Programmatic access

Alpine exposes under-the-hood utilities for getting and setting properties bound with `x-model`. This is useful for complex Alpine utilities that may want to override the default x-model behavior, or instances where you want to allow `x-model` on a non-input element.

You can access these utilities through a property called `_x_model` on the `x-model`ed element. `_x_model` has two methods to get and set the bound property:

* `el._x_model.get()` (returns the value of the bound property)
* `el._x_model.set()` (sets the value of the bound property)

```alpine
<div x-data="{ username: 'calebporzio' }">
<div x-ref="div" x-model="username"></div>
<button @click="$refs.div._x_model.set('phantomatrix')">
Change username to: 'phantomatrix'
</button>
<span x-text="$refs.div._x_model.get()"></span>
</div>
```

<!-- START_VERBATIM -->
<div class="demo">
<div x-data="{ username: 'calebporzio' }">
<div x-ref="div" x-model="username"></div>

<button @click="$refs.div._x_model.set('phantomatrix')">
Change username to: 'phantomatrix'
</button>

<span x-text="$refs.div._x_model.get()"></span>
</div>
</div>
<!-- END_VERBATIM -->
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/on.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 5
title: on
---

# `x-on`
# x-on

`x-on` allows you to easily run code on dispatched DOM events.

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/ref.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 11
title: ref
---

# `x-ref`
# x-ref

`x-ref` in combination with `$refs` is a useful utility for easily accessing DOM elements directly. It's most useful as a replacement for APIs like `getElementById` and `querySelector`.

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/show.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 3
title: show
---

# `x-show`
# x-show

`x-show` is one of the most useful and powerful directives in Alpine. It provides an expressive way to show and hide DOM elements.

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/text.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 6
title: text
---

# `x-text`
# x-text

`x-text` sets the text content of an element to the result of a given expression.

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/directives/transition.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ order: 10
title: transition
---

# `x-transition`
# x-transition

Alpine provides a robust transitions utility out of the box. With a few `x-transition` directives, you can create smooth transitions between when an element is shown or hidden.

Expand Down
18 changes: 18 additions & 0 deletions tests/cypress/integration/directives/x-model.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,21 @@ test('x-model trims value if trim modifier is present',
get('div').should(haveData('foo', 'bar'))
}
)

test('x-model can be accessed programmatically',
html`
<div x-data="{ foo: 'bar' }" x-model="foo">
<input x-model="foo">
<span x-text="$root._x_model.get()"></span>
<button @click="$root._x_model.set('bob')">Set foo to bob</button>
</div>
`,
({ get }) => {
get('span').should(haveText('bar'))
get('input').type('baz')
get('span').should(haveText('barbaz'))
get('button').click()
get('span').should(haveText('bob'))
}
)

0 comments on commit 98805c3

Please sign in to comment.