diff --git a/packages/alpinejs/src/directives/x-modelable.js b/packages/alpinejs/src/directives/x-modelable.js index 543e66827..583de85bd 100644 --- a/packages/alpinejs/src/directives/x-modelable.js +++ b/packages/alpinejs/src/directives/x-modelable.js @@ -1,7 +1,8 @@ import { directive } from '../directives' import { entangle } from '../entangle'; +import { skipDuringClone } from '../clone' -directive('modelable', (el, { expression }, { effect, evaluateLater, cleanup }) => { +directive('modelable', skipDuringClone((el, { expression }, { effect, evaluateLater, cleanup }) => { let func = evaluateLater(expression) let innerGet = () => { let result; func(i => result = i); return result; } let evaluateInnerSet = evaluateLater(`${expression} = __placeholder`) @@ -36,4 +37,4 @@ directive('modelable', (el, { expression }, { effect, evaluateLater, cleanup }) cleanup(releaseEntanglement) }) -}) +})) diff --git a/tests/cypress/integration/plugins/morph.spec.js b/tests/cypress/integration/plugins/morph.spec.js index 65b0eed69..7d17d6826 100644 --- a/tests/cypress/integration/plugins/morph.spec.js +++ b/tests/cypress/integration/plugins/morph.spec.js @@ -1387,3 +1387,64 @@ test('morphing does not re-apply x-cloak to an already initialized element', cy.window().its('heightsDuringMorph').should('have.length.greaterThan', 0).and('not.include', 0) }, ) + +test('x-modelable keeps a change made right after a morph', + [html` +
+
+ + +
+ `], + ({ get }, reload, window) => { + get('span').should(haveText('0')) + + get('#counter').then(([el]) => { + window.Alpine.morph(el, el.outerHTML) + + window.Alpine.$data(el).value++ + }) + + get('span').should(haveText('1')) + }, +) + +test('morphing an x-modelable element does not leave a binding behind', + [html` +
+
+ + +
+ `], + ({ get }, reload, window) => { + let readsBeforeMorphing + + get('span').should(haveText('0')) + + get('#counter').then(([el]) => { + window.reads = 0 + + window.Alpine.$data(el).value++ + }) + + get('span').should(haveText('1')) + + get('#counter').then(([el]) => { + readsBeforeMorphing = window.reads + + for (let i = 0; i < 5; i++) window.Alpine.morph(el, el.outerHTML) + + // Let the morphs settle before changing the value again... + window.setTimeout(() => { + window.reads = 0 + + window.Alpine.$data(el).value++ + }) + }) + + get('span').should(haveText('2')) + + cy.window().then(() => expect(window.reads).to.equal(readsBeforeMorphing)) + }, +)