Acceder a los datos de los hijos desde el padre con AlpineJS

AlpineJS permite estructuras de datos anidadas con x-data, lo que facilita el acceso a los datos del padre desde el hijo. Sin embargo, surge la pregunta: ¿es posible acceder a los datos de los hijos desde su padre?

Ejemplo concreto

En el siguiente código se presenta un ejemplo de cómo contar la cantidad de productos disponibles y vendidos usando clases CSS:

<script defer src="https://cdn.jsdelivr.net/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>

En este ejemplo, el estado de los hijos se convierte en una clase .avail que el padre puede detectar analizando el DOM. Sin embargo, este enfoque puede ser lento en páginas grandes.

Solución propuesta

Para evitar la detección a través de clases CSS, se puede modificar la estructura de datos para que los componentes hijos notifiquen a su padre sobre su estado. Esto se puede lograr utilizando eventos personalizados en AlpineJS.

  1. Emisión de Eventos: Cada componente hijo puede emitir un evento al padre cada vez que su estado cambie.
  2. Escucha de Eventos: El componente padre puede escuchar estos eventos y actualizar sus contadores en respuesta.

Implementación de la solución

A continuación se presenta una implementación que resuelve el problema sin usar clases CSS:

<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>

<div x-data="{
      children: [],
      addChild(avail) {
        this.children.push(avail);
        this.updateCounts();
      },
      updateCounts() {
        this.nAvailable = this.children.filter(x => x).length;
        this.nSoldOut = this.children.length - this.nAvailable;
      },
      nAvailable: 0,
      nSoldOut: 0,
    }">

  <div x-data="{ avail: true, $parent: $parent }" 
       x-init="$parent.addChild(avail)" 
       x-bind:class="{ 'avail': avail }">
    Product 1
  </div>
  <div x-data="{ avail: true, $parent: $parent }" 
       x-init="$parent.addChild(avail)" 
       x-bind:class="{ 'avail': avail }">
    Product 2
  </div>
  <div x-data="{ avail: true, $parent: $parent }" 
       x-init="$parent.addChild(avail)" 
       x-bind:class="{ 'avail': avail }">
    Product 3
  </div>
  <div x-data="{ avail: true, $parent: $parent }" 
       x-init="$parent.addChild(avail)" 
       x-bind:class="{ 'avail': avail }">
    Product 4
  </div>
  <div x-data="{ avail: false, $parent: $parent }" 
       x-init="$parent.addChild(avail)" 
       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>

Conclusión

Utilizando eventos personalizados, es posible acceder al estado de los elementos hijos sin necesidad de depender de la clase CSS. Esta metodología no solo mejora la eficiencia, sino que también mantiene el estado más centralizado y fácil de gestionar en aplicaciones más grandes.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *