Search code examples
laravel-livewirealpine.js

Alpine JS Error thrown when updating Entangled Livewire array data


Here is a contrived example of Alpine counters that are entangled with Livewire component data.

Here is my Livewire component:

<?php

namespace App\Http\Livewire;

use Livewire\Component;

class Foo extends Component
{
    public array $foo = [
        ['id'=>1,'bar'=>10],
        ['id'=>2,'bar'=>11],
        ['id'=>3,'bar'=>12]
    ];

    public function render()
    {
        return view('livewire.foo');
    }
}

And within the Blade template:

<div x-data="{ foo: @entangle('foo') }">
    <template x-for="f in foo" :key="f.id">
        <div x-data="{num: 0}" x-model="f.bar" x-modelable="num">
            Foo Counter Value: <div x-text="num"></div>
            <div @click="num ++">Increase</div>
        </div>
    </template>
</div>

If I click any Increase for a counter, it throws this JS error after the Livewire server request returns:

module.esm.js?027e:416 Alpine Expression Error: num is not defined

Expression: "num"

 <div x-data=​"{num:​ 0}​" x-model=​"f.bar" x-modelable=​"num">​…​</div>​
handleError @ module.esm.js?027e:416
eval @ module.esm.js?027e:489
Promise.catch (async)
eval @ module.esm.js?027e:489
tryCatch @ module.esm.js?027e:409
innerGet @ module.esm.js?027e:2233
eval @ module.esm.js?027e:2239
flushHandlers @ module.esm.js?027e:546
stopDeferring @ module.esm.js?027e:551
deferHandlingDirectives @ module.esm.js?027e:554
initTree @ module.esm.js?027e:764
eval @ module.esm.js?027e:2778
mutateDom @ module.esm.js?027e:153
eval @ module.esm.js?027e:2776
runIfTypeOfFunction @ module.esm.js?027e:510
eval @ module.esm.js?027e:491
tryCatch @ module.esm.js?027e:409
loop @ module.esm.js?027e:2692
eval @ module.esm.js?027e:2682
reactiveEffect @ module.esm.js?027e:1571
effect2 @ module.esm.js?027e:1546
effect @ module.esm.js?027e:48
eval @ module.esm.js?027e:1178
wrappedEffect @ module.esm.js?027e:64
eval @ module.esm.js?027e:2682
flushHandlers @ module.esm.js?027e:546
stopDeferring @ module.esm.js?027e:551
deferHandlingDirectives @ module.esm.js?027e:554
initTree @ module.esm.js?027e:764
cloneTree @ module.esm.js?027e:1173
eval @ module.esm.js?027e:1159
dontRegisterReactiveSideEffects @ module.esm.js?027e:1183
clone @ module.esm.js?027e:1158
alpinifyElementsForMorphdomV3 @ SupportAlpine.js:294
alpinifyElementsForMorphdom @ SupportAlpine.js:250
onBeforeElUpdated @ index.js:479
callHook @ morphdom.js:35
morphEl @ morphdom.js:199
(anonymous) @ morphdom.js:332
morphEl @ morphdom.js:219
(anonymous) @ morphdom.js:463
value @ index.js:386
value @ index.js:291
value @ index.js:269
value @ index.js:11
(anonymous) @ index.js:69
Promise.then (async)
(anonymous) @ index.js:64
Promise.then (async)
value @ index.js:62
sendMessage @ index.js:242
value @ index.js:252
later @ debounce.js:8
setTimeout (async)
(anonymous) @ debounce.js:12
value @ index.js:225
value @ index.js:165
(anonymous) @ SupportAlpine.js:202
reactiveEffect @ module.esm.js?027e:1571
flushJobs @ module.esm.js?027e:28
Show 15 more frames
module.esm.js?027e:420
Uncaught ReferenceError: num is not defined
    at eval (eval at safeAsyncFunction (module.esm.js:1:1), <anonymous>:3:32)
    at eval (module.esm.js?027e:489:1)
    at tryCatch (module.esm.js?027e:409:1)
    at innerGet (module.esm.js?027e:2233:1)
    at Function.eval (module.esm.js?027e:2239:1)
    at flushHandlers (module.esm.js?027e:546:1)
    at stopDeferring (module.esm.js?027e:551:1)
    at deferHandlingDirectives (module.esm.js?027e:554:1)
    at initTree (module.esm.js?027e:764:1)
    at eval (module.esm.js?027e:2778:1)
eval @ VM3130:3
eval @ module.esm.js?027e:489
tryCatch @ module.esm.js?027e:409
innerGet @ module.esm.js?027e:2233
eval @ module.esm.js?027e:2239
flushHandlers @ module.esm.js?027e:546
stopDeferring @ module.esm.js?027e:551
deferHandlingDirectives @ module.esm.js?027e:554
initTree @ module.esm.js?027e:764
eval @ module.esm.js?027e:2778
mutateDom @ module.esm.js?027e:153
eval @ module.esm.js?027e:2776
runIfTypeOfFunction @ module.esm.js?027e:510
eval @ module.esm.js?027e:491
tryCatch @ module.esm.js?027e:409
loop @ module.esm.js?027e:2692
eval @ module.esm.js?027e:2682
reactiveEffect @ module.esm.js?027e:1571
effect2 @ module.esm.js?027e:1546
effect @ module.esm.js?027e:48
eval @ module.esm.js?027e:1178
wrappedEffect @ module.esm.js?027e:64
eval @ module.esm.js?027e:2682
flushHandlers @ module.esm.js?027e:546
stopDeferring @ module.esm.js?027e:551
deferHandlingDirectives @ module.esm.js?027e:554
initTree @ module.esm.js?027e:764
cloneTree @ module.esm.js?027e:1173
eval @ module.esm.js?027e:1159
dontRegisterReactiveSideEffects @ module.esm.js?027e:1183
clone @ module.esm.js?027e:1158
alpinifyElementsForMorphdomV3 @ SupportAlpine.js:294
alpinifyElementsForMorphdom @ SupportAlpine.js:250
onBeforeElUpdated @ index.js:479
callHook @ morphdom.js:35
morphEl @ morphdom.js:199
(anonymous) @ morphdom.js:332
morphEl @ morphdom.js:219
(anonymous) @ morphdom.js:463
value @ index.js:386
value @ index.js:291
value @ index.js:269
value @ index.js:11
(anonymous) @ index.js:69
setTimeout (async)
handleError @ module.esm.js?027e:419
eval @ module.esm.js?027e:489
Promise.catch (async)
eval @ module.esm.js?027e:489
tryCatch @ module.esm.js?027e:409
innerGet @ module.esm.js?027e:2233
eval @ module.esm.js?027e:2239
flushHandlers @ module.esm.js?027e:546
stopDeferring @ module.esm.js?027e:551
deferHandlingDirectives @ module.esm.js?027e:554
initTree @ module.esm.js?027e:764
eval @ module.esm.js?027e:2778
mutateDom @ module.esm.js?027e:153
eval @ module.esm.js?027e:2776
runIfTypeOfFunction @ module.esm.js?027e:510
eval @ module.esm.js?027e:491
tryCatch @ module.esm.js?027e:409
loop @ module.esm.js?027e:2692
eval @ module.esm.js?027e:2682
reactiveEffect @ module.esm.js?027e:1571
effect2 @ module.esm.js?027e:1546
effect @ module.esm.js?027e:48
eval @ module.esm.js?027e:1178
wrappedEffect @ module.esm.js?027e:64
eval @ module.esm.js?027e:2682
flushHandlers @ module.esm.js?027e:546
stopDeferring @ module.esm.js?027e:551
deferHandlingDirectives @ module.esm.js?027e:554
initTree @ module.esm.js?027e:764
cloneTree @ module.esm.js?027e:1173
eval @ module.esm.js?027e:1159
dontRegisterReactiveSideEffects @ module.esm.js?027e:1183
clone @ module.esm.js?027e:1158
alpinifyElementsForMorphdomV3 @ SupportAlpine.js:294
alpinifyElementsForMorphdom @ SupportAlpine.js:250
onBeforeElUpdated @ index.js:479
callHook @ morphdom.js:35
morphEl @ morphdom.js:199
(anonymous) @ morphdom.js:332
morphEl @ morphdom.js:219
(anonymous) @ morphdom.js:463
value @ index.js:386
value @ index.js:291
value @ index.js:269
value @ index.js:11
(anonymous) @ index.js:69
Promise.then (async)
(anonymous) @ index.js:64
Promise.then (async)
value @ index.js:62
sendMessage @ index.js:242
value @ index.js:252
later @ debounce.js:8
setTimeout (async)
(anonymous) @ debounce.js:12
value @ index.js:225
value @ index.js:165
(anonymous) @ SupportAlpine.js:202
reactiveEffect @ module.esm.js?027e:1571
flushJobs @ module.esm.js?027e:28
Show 57 more frames

NOTE: this actually seems to work just fine. The counters increase, but this JS error gets thrown. It only seems to throw the error the first time you click one of the Increase buttons.


Solution

  • I just needed to add wire:ignore to the div containing x-data so that Livewire doesn't try to update the DOM. This is fine since Alpine manages the DOM with everything needed.

    <div x-data="{ foo: @entangle('foo') }" wire:ignore>
        <template x-for="f in foo" :key="f.id">
            <div x-data="{num: 0}" x-model="f.bar" x-modelable="num">
                Foo Counter Value: <div x-text="num"></div>
                <div @click="num ++">Increase</div>
            </div>
        </template>
    </div>