The following test fails with the CSP build:
test.csp('event object is not passed if other params are present',
[html`
<div x-data="test">
<button x-on:click="baz('foo')"></button>
<span x-text="foo"></span>
</div>
`,`
Alpine.data('test', () => ({
foo: 'bar',
baz(word) { this.foo = word }
}))
`],
({ get }) => {
get('span').should(haveText('bar'))
get('button').click()
get('span').should(haveText('foo'))
}
)
You can work around the issue by passing arguments through data attributes:
test.csp('arguments can be passed through data attributes',
[html`
<div x-data="test">
<button x-on:click="baz" data-baz-word="foo"></button>
<span x-text="foo"></span>
</div>
`,`
Alpine.data('test', () => ({
foo: 'bar',
baz() { this.foo = this.$el.dataset.bazWord }
}))
`],
({ get }) => {
get('span').should(haveText('bar'))
get('button').click()
get('span').should(haveText('foo'))
}
)
The following test fails with the CSP build:
test.csp('nested data modified in event listener updates affected attribute bindings',
[html`
<div x-data="test">
<button x-on:click="change"></button>
<span x-bind:foo="nested.foo"></span>
</div>
`,`
Alpine.data('test', () => ({
nested: { foo: 'bar' },
change() { this.nested.foo = 'baz' }
}))
`],
({ get }) => {
get('span').should(haveAttribute('foo', 'bar'))
get('button').click()
get('span').should(haveAttribute('foo', 'baz'))
}
)
You can work around the issue by creating a method that accesses the nested attribute:
test.csp('nested data modified in event listener updates affected attribute bindings',
[html`
<div x-data="test">
<button x-on:click="change"></button>
<span x-bind:foo="nestedDotFoo"></span>
</div>
`,`
Alpine.data('test', () => ({
nested: { foo: 'bar' },
nestedDotFoo() { return this.nested.foo },
change() { this.nested.foo = 'baz' }
}))
`],
({ get }) => {
get('span').should(haveAttribute('foo', 'bar'))
get('button').click()
get('span').should(haveAttribute('foo', 'baz'))
}
)
The following test fails with the CSP build:
test.csp('x-model updates value when updated via input event',
[html`
<div x-data="test">
<input x-model="foo"></input>
<span x-text="foo"></span>
</div>
`,
`
Alpine.data('test', () => ({
foo: 'bar'
}))
`],
({ get }) => {
get('span').should(haveText('bar'))
get('input').type('baz')
get('span').should(haveText('barbaz'))
}
)
I was able to work around this issue with the changes in alpine-csp.patch
(note: the patch hasn’t be thoroughly tested).
The following test fails with the CSP build:
test.csp('renders loops with x-for',
[html`
<div x-data="test">
<template x-for="item in items">
<span x-text="item.name"></span>
</template>
</div>
`,`
Alpine.data('test', () => ({
items: [{name: 'foo', value: 1}]
}))
`],
({ get }) => {
get('span:nth-of-type(1)').should(haveText('foo'))
}
)
In order to access items (or indices) generated by x-for
, you can use the $data
variable:
test.csp('renders loops with x-for',
[html`
<div x-data="test">
<template x-for="item in items">
<span x-text="itemName"></span>
</template>
</div>
`,`
Alpine.data('test', () => ({
items: [{name: 'foo', value: 1}],
itemName() { return this.$data.item.name }
}))
`],
({ get }) => {
get('span:nth-of-type(1)').should(haveText('foo'))
}
)
The following test fails with the CSP build:
test.csp('nested x-for',
[html`
<div x-data="test">
<button x-on:click="change">click me</button>
<template x-for="foo in foos">
<h1>
<template x-for="bar in foo.bars">
<h2 x-text="bar"></h2>
</template>
</h1>
</template>
</div>
`,`
Alpine.data('test', () => ({
foos: [ {bars: ['bob', 'lob']} ],
change() { this.foos = [ {bars: ['bob', 'lob']}, {bars: ['law']} ] },
}))
`],
({ get }) => {
get('h1:nth-of-type(1) h2:nth-of-type(1)').should(exist())
get('h1:nth-of-type(1) h2:nth-of-type(2)').should(exist())
get('h1:nth-of-type(2) h2:nth-of-type(1)').should(notExist())
get('button').click()
get('h1:nth-of-type(1) h2:nth-of-type(1)').should(exist())
get('h1:nth-of-type(1) h2:nth-of-type(2)').should(exist())
get('h1:nth-of-type(2) h2:nth-of-type(1)').should(exist())
}
)
You can nest x-for
s by created a method to access the nested elements:
test.csp('nested x-for',
[html`
<div x-data="test">
<button x-on:click="change">click me</button>
<template x-for="foo in foos">
<h1>
<template x-for="bar in fooDotBars">
<h2 x-text="bar"></h2>
</template>
</h1>
</template>
</div>
`,`
Alpine.data('test', () => ({
foos: [ {bars: ['bob', 'lob']} ],
fooDotBars() { return this.$data.foo.bars },
change() { this.foos = [ {bars: ['bob', 'lob']}, {bars: ['law']} ] },
}))
`],
({ get }) => {
get('h1:nth-of-type(1) h2:nth-of-type(1)').should(exist())
get('h1:nth-of-type(1) h2:nth-of-type(2)').should(exist())
get('h1:nth-of-type(2) h2:nth-of-type(1)').should(notExist())
get('button').click()
get('h1:nth-of-type(1) h2:nth-of-type(1)').should(exist())
get('h1:nth-of-type(1) h2:nth-of-type(2)').should(exist())
get('h1:nth-of-type(2) h2:nth-of-type(1)').should(exist())
}
)
The following test fails with the CSP build:
test.csp('x-for updates the right elements when new item are inserted at the beginning of the list',
[html`
<div x-data="test">
<button x-on:click="change">click me</button>
<template x-for="item in items" :key="item.key">
<span x-text="itemDotName"></span>
</template>
</div>
`,`
Alpine.data('test', () => ({
items: [{name: 'one', key: '1'}, {name: 'two', key: '2'}],
itemDotName() { return this.$data.item.name },
change() { this.items = [{name: 'zero', key: '0'}, {name: 'one', key: '1'}, {name: 'two', key: '2'}] },
}))
`],
({ get }) => {
get('span:nth-of-type(1)').should(haveText('one'))
get('span:nth-of-type(2)').should(haveText('two'))
get('button').click()
get('span:nth-of-type(1)').should(haveText('zero'))
get('span:nth-of-type(2)').should(haveText('one'))
get('span:nth-of-type(3)').should(haveText('two'))
}
)
I was able to work around this issue with the following patch (note: the patch hasn’t be thoroughly tested):
Git Patch
diff --git a/packages/alpinejs/src/directives/x-for.js b/packages/alpinejs/src/directives/x-for.js
index 0521336..ba5ab57 100644
--- a/packages/alpinejs/src/directives/x-for.js
+++ b/packages/alpinejs/src/directives/x-for.js
@@ -60,7 +60,7 @@ function loop(el, iteratorNames, evaluateItems, evaluateKey) {
items = Object.entries(items).map(([key, value]) => {
let scope = getIterationScopeVariables(iteratorNames, value, key, items)
- evaluateKey(value => keys.push(value), { scope: { index: key, ...scope} })
+ evaluateKey(value => keys.push(value), { scope: { index: key, ...scope}, params: [value] } )
scopes.push(scope)
})
@@ -68,7 +68,7 @@ function loop(el, iteratorNames, evaluateItems, evaluateKey) {
for (let i = 0; i < items.length; i++) {
let scope = getIterationScopeVariables(iteratorNames, items[i], i, items)
- evaluateKey(value => keys.push(value), { scope: { index: i, ...scope} })
+ evaluateKey(value => keys.push(value), { scope: { index: i, ...scope}, params: [items[i]] })
scopes.push(scope)
}
And specify a function to retrieve the key:
test.csp('x-for updates the right elements when new item are inserted at the beginning of the list',
[html`
<div x-data="test">
<button x-on:click="change">click me</button>
<template x-for="item in items" :key="itemDotKey">
<span x-text="itemDotName"></span>
</template>
</div>
`,`
Alpine.data('test', () => ({
items: [{name: 'one', key: '1'}, {name: 'two', key: '2'}],
itemDotKey(item) { return item.key },
itemDotName() { return this.$data.item.name },
change() { this.items = [{name: 'zero', key: '0'}, {name: 'one', key: '1'}, {name: 'two', key: '2'}] },
}))
`],
({ get }) => {
get('span:nth-of-type(1)').should(haveText('one'))
get('span:nth-of-type(2)').should(haveText('two'))
get('button').click()
get('span:nth-of-type(1)').should(haveText('zero'))
get('span:nth-of-type(2)').should(haveText('one'))
get('span:nth-of-type(3)').should(haveText('two'))
}
)
The following test fails with the CSP build:
test.csp('x-for over range using i in x syntax',
[html`
<div x-data>
<template x-for="i in 10">
<span x-text="i"></span>
</template>
</div>
`],
({ get }) => get('span').should(haveLength('10'))
)
I was able to work around this issue with the patch from x-model
bound to an input
does not react to changes
The following test fails with the CSP build:
test.csp('renders children in the right order when combined with x-if',
[html`
<div x-data="test">
<template x-for="item in items">
<template x-if="true">
<span x-text="item"></span>
</template>
</template>
</div>
`,`
Alpine.data('test', () => ({
items: ['foo', 'bar']
}))
`],
({ get }) => {
get('span:nth-of-type(1)').should(haveText('foo'))
get('span:nth-of-type(2)').should(haveText('bar'))
}
)
You can fix it by either creating isTrue() { return true }
/isFalse() { return false }
method in your x-data
(and calling them instead of true
/ false
), or apply the patch from alpine-csp.patch
.
The following test fails with the CSP build:
test.csp('sets undefined nested keys to empty string',
[html`
<div x-data="test">
<span x-bind:foo="nestedDotField">
</div>
`,`
Alpine.data('test', () => ({
nested: {},
nestedDotField() { return this.nested.field }
}))
`],
({ get }) => get('span').should(haveAttribute('foo', ''))
)
A possible workaround is to explicitely return an empty string: nestedDotField() { return this.nested.field || '' }
.
The following test fails with the CSP build:
test.csp('x-bind object syntax supports normal html attributes',
[html`
<span x-data="test" x-bind="value" x-text="text"></span>
`,`
Alpine.data('test', () => ({
value: { foo: 'bar' },
text: 'text'
}))
`],
({ get }) => {
get('span').should(haveAttribute('foo', 'bar'))
}
)
You can fix it by applying the patch from alpine-csp.patch
.