Skip to content

Commit

Permalink
Bug: Updates correct scope when x-for looping over element with x-data (
Browse files Browse the repository at this point in the history
#3504)

* 🧪 Adds test for failing scope conflict

* ✅ Passes x-for scope test

* Refactor implementation

---------

Co-authored-by: Caleb Porzio <calebporzio@gmail.com>
  • Loading branch information
ekwoka and calebporzio committed May 11, 2023
1 parent b6917d9 commit 117668f
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 16 deletions.
16 changes: 12 additions & 4 deletions packages/alpinejs/src/directives/x-for.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { addScopeToNode, refreshScope } from '../scope'
import { addScopeToNode } from '../scope'
import { evaluateLater } from '../evaluator'
import { directive } from '../directives'
import { reactive } from '../reactivity'
Expand Down Expand Up @@ -168,7 +168,7 @@ function loop(el, iteratorNames, evaluateItems, evaluateKey) {
marker.remove()
})

refreshScope(elForSpot, scopes[keys.indexOf(keyForSpot)])
elForSpot._x_refreshXForScope(scopes[keys.indexOf(keyForSpot)])
}

// We can now create and add new elements.
Expand All @@ -185,7 +185,15 @@ function loop(el, iteratorNames, evaluateItems, evaluateKey) {

let clone = document.importNode(templateEl.content, true).firstElementChild

addScopeToNode(clone, reactive(scope), templateEl)
let reactiveScope = reactive(scope)

addScopeToNode(clone, reactiveScope, templateEl)

clone._x_refreshXForScope = (newScope) => {
Object.entries(newScope).forEach(([key, value]) => {
reactiveScope[key] = value
})
}

mutateDom(() => {
lastEl.after(clone)
Expand All @@ -204,7 +212,7 @@ function loop(el, iteratorNames, evaluateItems, evaluateKey) {
// data it depends on in case the data has changed in an
// "unobservable" way.
for (let i = 0; i < sames.length; i++) {
refreshScope(lookup[sames[i]], scopes[keys.indexOf(sames[i])])
lookup[sames[i]]._x_refreshXForScope(scopes[keys.indexOf(sames[i])])
}

// Now we'll log the keys (and the order they're in) for comparing
Expand Down
12 changes: 2 additions & 10 deletions packages/alpinejs/src/scope.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,6 @@ export function hasScope(node) {
return !! node._x_dataStack
}

export function refreshScope(element, scope) {
let existingScope = element._x_dataStack[0]

Object.entries(scope).forEach(([key, value]) => {
existingScope[key] = value
})
}

export function closestDataStack(node) {
if (node._x_dataStack) return node._x_dataStack

Expand Down Expand Up @@ -60,7 +52,7 @@ export function mergeProxies(objects) {
if ((descriptor.get && descriptor.get._x_alreadyBound) || (descriptor.set && descriptor.set._x_alreadyBound)) {
return true
}

// Properly bind getters and setters to this wrapper Proxy.
if ((descriptor.get || descriptor.set) && descriptor.enumerable) {
// Only bind user-defined getters, not our magic properties.
Expand All @@ -81,7 +73,7 @@ export function mergeProxies(objects) {
})
}

return true
return true
}

return false
Expand Down
24 changes: 22 additions & 2 deletions tests/cypress/integration/directives/x-for.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,27 @@ test('renders children using directives injected by x-html correctly',
}
)

test(
'handles x-data directly inside x-for',
html`
<div x-data="{ items: [{x:0, k:1},{x:1, k:2}] }">
<button x-on:click="items = [{x:3, k:1},{x:4, k:2}]">update</button>
<template x-for="item in items" :key="item.k">
<div :id="'item-' + item.k" x-data="{ inner: true }">
<span x-text="item.x.toString()"></span>:
<span x-text="item.k"></span>
</div>
</template>
</div>
`,
({ get }) => {
get('#item-1 span:nth-of-type(1)').should(haveText('0'))
get('#item-2 span:nth-of-type(1)').should(haveText('1'))
get('button').click()
get('#item-1 span:nth-of-type(1)').should(haveText('3'))
get('#item-2 span:nth-of-type(1)').should(haveText('4'))
})

test('x-for throws descriptive error when key is undefined',
html`
<div x-data="{ items: [
Expand All @@ -581,7 +602,6 @@ test('x-for throws descriptive error when key is undefined',
</template>
</div>
`,
({ get }) => {
},
({ get }) => {},
true
)

0 comments on commit 117668f

Please sign in to comment.