Alpins JS x-data overwritten

I have the following HTML structure:

<div x-data="{selected: 0}">
    <div x-data="{ open: false }"
        @click="open = !open; selected !== 1 ? selected = 1 : selected = 0;"
    >
        test
    </div>
    <div x-data="{ open: false }"
        @click="open = !open; selected !== 2 ? selected = 2 : selected = 0;"
    >
        test
    </div>
</div>

The problem is the the first x-data with the selected is no longer there after I write in the div children x-data. I think it is right to say I overwrite it. So the question is, how can I prevent this from happening? I want to be able to keep the open and also to update the selected. thank you

2 answers

  • answered 2021-05-05 10:54 ptts

    Alpine.js, unlike React or Vue, doesn’t provide any parent-child communication patterns or primitives. “Alpine components” don’t receive any context from parent components, each component has its own individual and independent scope which means nesting doesn’t work as you would expect coming from Vue/React or even JavaScript (JavaScript has lexical scoping, Alpine does not).

    That means that in Alpine.js there’s no parent-child component communication only “sibling” component communication. For sibling communication, the pattern often proposed in issues by Caleb (the creator of Alpine.js) and other contributors is to use the window or document as an event bus.

    Source: https://codewithhugo.com/alpinejs-component-communication-event-bus/

    Here is a code example that they name:

    <div x-data="{ isOpen: false }">
      <!-- this component can be shown/hidden using a `toggle` event  -->
      <div
        x-show="isOpen"
        x-on:toggle.window="isOpen = !isOpen"
        role="alert"
        >
        <p>This alert is toggled when `toggle` events are dispatched.</p>
        <button @click="isOpen = false">close alert</button>
      </div>
    </div>
    <div x-data="{}">
      <p>The button in this component can toggle the "alert".</p>
      <button @click="$dispatch('toggle')">Toggle alert</button>
    </div>
    <div x-data="{}">
      <p>The button in this component can <strong>also</strong> toggle the "alert".</p>
      <button @click="$dispatch('toggle')">Toggle alert 2</button>
    </div>
    
    

  • answered 2021-05-06 06:15 Morpheus_ro

    I did a lot of research yesterday and yes, AlpineJS is not as React/Vue/Angular but I have a working solution and this is something like this:

    <div
        x-data="{ count : 0 }"
        x-init="$watch('count', val => $dispatch('new-count', val))"
        x-on:increment="count++"
        x-on:decrement="count--"
    >
        <div>In root component: <span x-text="count"></span></div>
            <div
            x-data="{ count: 0 }"
            x-on:new-count.window="count = $event.detail"
        >
            <div>In nested component <span x-text="count"></span></div>
            <button x-on:click="$dispatch('increment')">Increment</button>
            <button x-on:click="$dispatch('decrement')">Decrement</button>
        </div>
    </div>
    

    Use a $dispatch and in the parent use a $watch and inside it make another $dispatch with another event name. The piece of the puzzle is to listen to the event but on the window level. Not ideal, but working. I hope it helps also someone else.