WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 4062724

Browse files
committed
Add short syntax: @ and : && fix class binding to string output
1 parent 4b49315 commit 4062724

File tree

7 files changed

+68
-8
lines changed

7 files changed

+68
-8
lines changed

dist/alpine.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/alpine.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"main": "dist/alpine.js",
33
"name": "alpinejs",
4-
"version": "1.4.0",
4+
"version": "1.5.0",
55
"repository": {
66
"type": "git",
77
"url": "git://github.com/alpinejs/alpine.git"

src/component.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,9 @@ export default class Component {
415415
el.value = value
416416
}
417417
} else if (attrName === 'class') {
418-
if (Array.isArray(value)) {
418+
if (typeof value === 'string') {
419+
el.setAttribute('class', value)
420+
} else if (Array.isArray(value)) {
419421
el.setAttribute('class', value.join(' '))
420422
} else {
421423
// Use the class object syntax that vue uses to toggle them.

src/utils.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,22 @@ export function saferEvalNoReturn(expression, dataContext, additionalHelperVaria
6666
}
6767

6868
export function isXAttr(attr) {
69+
const name = replaceAtAndColonWithStandardSyntax(attr.name)
70+
6971
const xAttrRE = /x-(on|bind|data|text|html|model|if|show|cloak|transition|ref)/
7072

71-
return xAttrRE.test(attr.name)
73+
return xAttrRE.test(name)
7274
}
7375

7476
export function getXAttrs(el, type) {
7577
return Array.from(el.attributes)
7678
.filter(isXAttr)
7779
.map(attr => {
78-
const typeMatch = attr.name.match(/x-(on|bind|data|text|html|model|if|show|cloak|transition|ref)/)
79-
const valueMatch = attr.name.match(/:([a-zA-Z\-]+)/)
80-
const modifiers = attr.name.match(/\.[^.\]]+(?=[^\]]*$)/g) || []
80+
const name = replaceAtAndColonWithStandardSyntax(attr.name)
81+
82+
const typeMatch = name.match(/x-(on|bind|data|text|html|model|if|show|cloak|transition|ref)/)
83+
const valueMatch = name.match(/:([a-zA-Z\-]+)/)
84+
const modifiers = name.match(/\.[^.\]]+(?=[^\]]*$)/g) || []
8185

8286
return {
8387
type: typeMatch ? typeMatch[1] : null,
@@ -94,6 +98,16 @@ export function getXAttrs(el, type) {
9498
})
9599
}
96100

101+
export function replaceAtAndColonWithStandardSyntax(name) {
102+
if (name.startsWith('@')) {
103+
return name.replace('@', 'x-on:')
104+
} else if (name.startsWith(':')) {
105+
return name.replace(':', 'x-bind:')
106+
}
107+
108+
return name
109+
}
110+
97111
export function transitionIn(el, callback, forceSkip = false) {
98112
if (forceSkip) callback()
99113

test/bind.spec.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Alpine from 'alpinejs'
2+
import { wait } from '@testing-library/dom'
23

34
global.MutationObserver = class {
45
observe() {}
@@ -102,6 +103,19 @@ test('class attribute bindings are added by array syntax', async () => {
102103
expect(document.querySelector('span').classList.contains('foo')).toBeTruthy
103104
})
104105

106+
test('class attribute bindings are synced by string syntax', async () => {
107+
document.body.innerHTML = `
108+
<div x-data="{foo: 'bar baz'}">
109+
<span class="" x-bind:class="foo"></span>
110+
</div>
111+
`
112+
113+
Alpine.start()
114+
115+
expect(document.querySelector('span').classList.contains('bar')).toBeTruthy
116+
expect(document.querySelector('span').classList.contains('baz')).toBeTruthy
117+
})
118+
105119
test('boolean attributes set to false are removed from element', async () => {
106120
document.body.innerHTML = `
107121
<div x-data="{ isSet: false }">
@@ -139,3 +153,15 @@ test('boolean attributes set to true are added to element', async () => {
139153
expect(document.querySelectorAll('input')[2].required).toBeTruthy()
140154
expect(document.querySelectorAll('input')[3].readOnly).toBeTruthy()
141155
})
156+
157+
test('binding supports short syntax', async () => {
158+
document.body.innerHTML = `
159+
<div x-data="{ foo: 'bar' }">
160+
<span :class="foo"></span>
161+
</div>
162+
`
163+
164+
Alpine.start()
165+
166+
expect(document.querySelector('span').classList.contains('bar')).toBeTruthy()
167+
})

test/on.spec.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,21 @@ test('click away', async () => {
205205

206206
await wait(() => { expect(document.querySelector('ul').classList.contains('hidden')).toEqual(false) })
207207
})
208+
209+
test('supports short syntax', async () => {
210+
document.body.innerHTML = `
211+
<div x-data="{ foo: 'bar' }">
212+
<button @click="foo = 'baz'"></button>
213+
214+
<span x-bind:foo="foo"></span>
215+
</div>
216+
`
217+
218+
Alpine.start()
219+
220+
expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
221+
222+
document.querySelector('button').click()
223+
224+
await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
225+
})

0 commit comments

Comments
 (0)