AlpineJS' x-data
can be nested, so one can access parent data inside a child.
But can one access child data from its parent?
Contrived example:
<script defer src="/[email protected]/dist/cdn.min.js"></script>
<div x-data="{ get nAvailable() { return $el.querySelectorAll('.product.avail').length },
get nSoldOut() { return $el.querySelectorAll('.product:not(.avail)').length },
}">
<div x-data="{ avail: true }" class="product" x-bind:class="avail && 'avail'">Product 1</div>
<div x-data="{ avail: true }" class="product" x-bind:class="avail && 'avail'">Product 2</div>
<div x-data="{ avail: true }" class="product" x-bind:class="avail && 'avail'">Product 3</div>
<div x-data="{ avail: true }" class="product" x-bind:class="avail && 'avail'">Product 4</div>
<div x-data="{ avail: false }" class="product" x-bind:class="avail && 'avail'">Product 5 <span x-text="avail || '(sold out)'"></span></div>
<br/>
<div>Available: <span x-text="nAvailable"></span></div>
<div>Sold out: <span x-text="nSoldOut"></span></div>
</div>
AlpineJS' x-data
can be nested, so one can access parent data inside a child.
But can one access child data from its parent?
Contrived example:
<script defer src="https://cdn.jsdelivr/npm/[email protected]/dist/cdn.min.js"></script>
<div x-data="{ get nAvailable() { return $el.querySelectorAll('.product.avail').length },
get nSoldOut() { return $el.querySelectorAll('.product:not(.avail)').length },
}">
<div x-data="{ avail: true }" class="product" x-bind:class="avail && 'avail'">Product 1</div>
<div x-data="{ avail: true }" class="product" x-bind:class="avail && 'avail'">Product 2</div>
<div x-data="{ avail: true }" class="product" x-bind:class="avail && 'avail'">Product 3</div>
<div x-data="{ avail: true }" class="product" x-bind:class="avail && 'avail'">Product 4</div>
<div x-data="{ avail: false }" class="product" x-bind:class="avail && 'avail'">Product 5 <span x-text="avail || '(sold out)'"></span></div>
<br/>
<div>Available: <span x-text="nAvailable"></span></div>
<div>Sold out: <span x-text="nSoldOut"></span></div>
</div>
That works.
But child state is converted to a class (.avail
) that the parent can detect by scanning the DOM. In a large page that is slow.
Is it possible to do this without that class?
Share Improve this question asked Mar 14 at 13:25 lonixlonix 21.4k29 gold badges139 silver badges281 bronze badges1 Answer
Reset to default 1Data typically flows downward from parent to child components, following a unidirectional data flow pattern. This "props down, events up" approach means that while parents can easily pass data to their children, accessing child state from a parent component usually requires additional mechanisms such as event emission, callbacks, or state lifting.
So, instead of trying to send state up, a better approach would be to maintain the state in the parent so that all child components can update:
<script defer src="https://cdn.jsdelivr/npm/[email protected]/dist/cdn.min.js"></script>
<div x-data="{
products: [
{ id: 1, name: 'Product 1', avail: true },
{ id: 2, name: 'Product 2', avail: true },
{ id: 3, name: 'Product 3', avail: true },
{ id: 4, name: 'Product 4', avail: true },
{ id: 5, name: 'Product 5', avail: false }
],
get nAvailable() {
return this.products.filter(p => p.avail).length
},
get nSoldOut() {
return this.products.filter(p => !p.avail).length
}
}">
<template x-for="product in products" :key="product.id">
<div class="product" x-bind:class="product.avail && 'avail'">
<span x-text="product.name"></span>
<span x-show="!product.avail"> (sold out)</span>
<!-- toggle btn -->
<button x-on:click="product.avail = !product.avail">Toggle Availability</button>
</div>
</template>
<br/>
<div>Available: <span x-text="nAvailable"></span></div>
<div>Sold out: <span x-text="nSoldOut"></span></div>
</div>
Here i'm using many alpine features so if in doubt please reefer to the docs