Alpine.store in Magento 2 con Hyvä: come condividere lo stato tra componenti Alpine.js

alpine store con magento 2 e hyva

Uno degli aspetti più interessanti dello sviluppo frontend con Magento 2 e Hyvä Themes è la possibilità di realizzare interfacce dinamiche utilizzando JavaScript leggero, senza ricorrere a framework complessi come React o Vue.

Hyvä utilizza Alpine.js per gestire l’interattività dei componenti frontend. Nella maggior parte dei casi, la direttiva x-data è sufficiente per implementare menu, modali, selettori e altri elementi dinamici.

Ma cosa succede quando più componenti, distribuiti in template Magento differenti, devono condividere le stesse informazioni?

Pensiamo a un e-commerce che deve visualizzare contemporaneamente:

  • Una barra che indica quanto manca per ottenere un regalo.
  • Un messaggio promozionale nel carrello.
  • Un popup per scegliere un prodotto omaggio.
  • Un badge sui prodotti che partecipano alla promozione.

Questi elementi possono trovarsi in blocchi Magento completamente indipendenti.

Una possibile soluzione consiste nell’utilizzare eventi JavaScript personalizzati. Tuttavia, quando il numero di componenti aumenta, la gestione degli eventi può diventare complessa.

Alpine.store() offre un’alternativa: uno stato globale reattivo, condiviso tra tutti i componenti Alpine.js presenti nella pagina.

Vediamo come funziona e come integrarlo correttamente in un progetto Magento 2 con Hyvä.

1. Cos’è Alpine.store e come funziona

Alpine.store() è una funzionalità disponibile in Alpine.js 3 che permette di definire oggetti JavaScript globali e reattivi.

A differenza di x-data, che crea uno stato associato a uno specifico elemento DOM, uno store può essere utilizzato da qualsiasi componente Alpine.js.

Consideriamo questo esempio:

document.addEventListener('alpine:init', () => {
    Alpine.store('cart', {
        itemsCount: 0,

        increment() {
            this.itemsCount++;
        }
    });
});

Abbiamo creato uno store chiamato cart, contenente:

  • Una proprietà reattiva itemsCount.
  • Un metodo increment() che modifica lo stato.

Per accedere allo store da un componente HTML utilizziamo $store.

<div x-data>
    Prodotti nel carrello:
    <span x-text="$store.cart.itemsCount"></span>
</div>

Un secondo componente può modificare lo stesso valore:

<div x-data>
    <button
        type="button"
        @click="$store.cart.increment()"
    >
        Incrementa
    </button>
</div>

Quando viene premuto il pulsante, il contatore del primo componente viene aggiornato automaticamente.

Non è necessario:

  • Cercare gli elementi con document.querySelector().
  • Generare eventi personalizzati.
  • Implementare listener per sincronizzare i componenti.
  • Aggiornare manualmente il DOM.

Alpine.js gestisce automaticamente la propagazione delle modifiche.

Differenza tra x-data e Alpine.store

La distinzione principale riguarda l’ambito dello stato.

Caratteristica x-data Alpine.store
Ambito Componente e discendenti Globale nella pagina
Reattività Sì Sì
Condivisione tra componenti indipendenti Non diretta Sì
Inizializzazione Attributo HTML o Alpine.data Alpine.store
Utilizzo ideale Stato locale Stato condiviso
Durata Ciclo di vita del componente Ciclo di vita della pagina

La regola architetturale è semplice:

Utilizziamo x-data quando lo stato appartiene a un componente. Utilizziamo Alpine.store() quando lo stato deve essere condiviso tra componenti indipendenti.

Non è necessario trasformare ogni componente Alpine.js in uno store globale.

2. Perché Alpine.store è particolarmente utile in Magento 2 con Hyvä

Magento 2 utilizza un sistema di layout XML e blocchi PHP per costruire le pagine frontend.

In un progetto Hyvä possiamo avere diversi template:

Magento_Theme/
    templates/
        html/
            header.phtml

Magento_Checkout/
    templates/
        cart/
            cart.phtml

Vendor_Promotion/
    templates/
        promo-banner.phtml
        gift-selector.phtml
        product-badge.phtml

Questi template possono essere renderizzati in posizioni differenti della pagina.

Ogni template può contenere un proprio componente Alpine.js:

<div x-data="initPromoBanner()">
    ...
</div>
<div x-data="initGiftSelector()">
    ...
</div>
<div x-data="initProductBadge()">
    ...
</div>

Il problema nasce quando tutti devono conoscere lo stesso stato applicativo.

Per esempio:

{
    cartTotal: 85,
    freeGiftThreshold: 100,
    selectedGift: null,
    isEligible: false
}

Una soluzione tradizionale potrebbe utilizzare CustomEvent e window.addEventListener() per distribuire le informazioni.

Con Alpine.store() possiamo invece centralizzare i dati:

Alpine.store('promotion', {
    cartTotal: 0,
    threshold: 100,
    selectedGift: null,

    get isEligible() {
        return this.cartTotal >= this.threshold;
    }
});

Ogni componente può accedere direttamente allo stato condiviso:

<div x-data>
    <span
        x-show="$store.promotion.isEligible"
    >
        Hai diritto a un regalo!
    </span>
</div>

Questa soluzione è particolarmente interessante per le funzionalità frontend che coinvolgono più blocchi Magento.

3. Esempio pratico: promozione con regalo al raggiungimento di una soglia

Realizziamo una funzionalità per Magento 2 con Hyvä.

Requisito funzionale: quando il subtotale del carrello raggiunge 100 euro, il cliente può selezionare un prodotto omaggio.

L’interfaccia prevede quattro componenti:

  1. Banner promozionale.
  2. Indicatore di avanzamento della spesa.
  3. Drawer per la selezione del regalo.
  4. Messaggio nel carrello.

Tutti i componenti devono utilizzare le stesse informazioni.

3.1 Architettura della soluzione

Possiamo rappresentare il flusso nel seguente modo:

             Magento 2
                 |
                 |
          Dati del carrello
                 |
                 v
         Evento update-totals
                 |
                 v
       +-------------------+
       |   Alpine.store    |
       |     promotion     |
       +-------------------+
                 |
        +--------+--------+
        |        |        |
        v        v        v
      Banner   Progress  Drawer
               Bar
        |
        v
   Cart Message

Lo store rappresenta la fonte condivisa dello stato frontend.

Ogni componente legge le proprietà che gli interessano e reagisce automaticamente alle modifiche.

Importante: lo store non deve diventare la fonte autorevole delle regole commerciali.

La validazione della soglia promozionale e dell’effettiva disponibilità del regalo deve essere eseguita anche dal backend Magento.

4. Implementare lo store Alpine.js

Creiamo uno store chiamato promotion.

Per semplicità utilizziamo JavaScript direttamente nel template. In un modulo Magento strutturato possiamo separare la logica in un file dedicato.

document.addEventListener('alpine:init', () => {

    if (Alpine.store('promotion')) {
        return;
    }

    Alpine.store('promotion', {

        // Configurazione
        threshold: 100,

        // Stato
        cartTotal: 0,
        selectedGift: null,
        drawerOpen: false,
        bannerVisible: true,

        // Proprietà calcolate
        get isEligible() {
            return this.cartTotal >= this.threshold;
        },

        get remaining() {
            return Math.max(
                0,
                this.threshold - this.cartTotal
            );
        },

        get progress() {
            if (this.threshold <= 0) {
                return 100;
            }

            return Math.min(
                100,
                (this.cartTotal / this.threshold) * 100
            );
        },

        // Azioni
        openDrawer() {
            this.drawerOpen = true;
        },

        closeDrawer() {
            this.drawerOpen = false;
        },

        selectGift(gift) {
            this.selectedGift = gift;
            this.closeDrawer();
        },

        dismissBanner() {
            this.bannerVisible = false;
        },

        // Sincronizzazione carrello
        updateCartTotal(total) {
            const previousEligibility = this.isEligible;

            this.cartTotal = Number(total) || 0;

            if (
                !previousEligibility &&
                this.isEligible &&
                !this.selectedGift
            ) {
                this.openDrawer();
            }

            if (!this.isEligible) {
                this.selectedGift = null;
                this.closeDrawer();
            }
        }
    });
});

Perché utilizzare i getter?

Le proprietà:

get isEligible()
get remaining()
get progress()

non rappresentano uno stato indipendente.

Sono valori derivati da altre proprietà.

Per esempio:

get remaining() {
    return Math.max(
        0,
        this.threshold - this.cartTotal
    );
}

Se cartTotal passa da 50 a 80, remaining restituisce automaticamente 20.

Non dobbiamo aggiornare manualmente entrambe le proprietà.

Questo approccio riduce il rischio di inconsistenze.

Evitare l’inizializzazione multipla

È importante verificare che lo store non sia già stato registrato:

if (Alpine.store('promotion')) {
    return;
}

In questo modo evitiamo di sovrascrivere accidentalmente uno stato già inizializzato.

La protezione è utile soprattutto nei progetti Magento con molti moduli custom e layout XML complessi.

5. Creare un banner promozionale reattivo

Il primo componente visualizza quanto manca per ottenere il regalo.

<div
    x-data
    x-show="$store.promotion.bannerVisible"
    x-cloak
    class="bg-blue-50 p-4 rounded-lg"
>
    <template x-if="!$store.promotion.isEligible">
        <div>
            <p>
                Aggiungi ancora
                <strong
                    x-text="
                        hyva.formatPrice(
                            $store.promotion.remaining
                        )
                    "
                ></strong>
                al carrello per ricevere un regalo.
            </p>
        </div>
    </template>

    <template x-if="$store.promotion.isEligible">
        <div>
            <p>
                Complimenti! Hai raggiunto
                la soglia per ottenere un regalo.
            </p>

            <button
                type="button"
                @click="$store.promotion.openDrawer()"
            >
                Scegli il regalo
            </button>
        </div>
    </template>
</div>

Il componente non contiene logica relativa al calcolo della promozione.

Si limita a leggere lo stato globale.

Questo consente di mantenere il template semplice.

Un vantaggio importante

Possiamo inserire il banner:

  • Nell’header.
  • Nella pagina prodotto.
  • Nel carrello.
  • In una sidebar.

Non è necessario duplicare la logica JavaScript.

Tutti i banner utilizzano lo stesso store.

6. Implementare una progress bar condivisa

Una barra di avanzamento permette al cliente di visualizzare quanto manca per raggiungere la soglia.

<div x-data class="w-full">

    <div
        class="w-full bg-gray-200 rounded-full h-3"
    >
        <div
            class="bg-green-600 h-3 rounded-full"
            :style="
                `width: ${$store.promotion.progress}%`
            "
        ></div>
    </div>

    <p class="mt-2 text-sm">

        <span
            x-text="$store.promotion.progress.toFixed(0)"
        ></span>%

        della soglia raggiunta

    </p>

</div>

La progress bar si aggiorna automaticamente quando cambia cartTotal.

Per esempio:

Subtotale carrello Avanzamento
20 € 20%
50 € 50%
75 € 75%
100 € 100%
150 € 100%

Il limite massimo viene gestito dal getter:

return Math.min(
    100,
    (this.cartTotal / this.threshold) * 100
);

In questo modo la barra non supera mai il 100%.

7. Gestire un drawer globale per la selezione del regalo

Supponiamo di voler visualizzare un drawer quando il cliente raggiunge la soglia promozionale.

Il drawer può essere definito in un template indipendente:

<div
    x-data
    x-show="$store.promotion.drawerOpen"
    x-cloak
    @keydown.escape.window="
        $store.promotion.closeDrawer()
    "
    class="fixed inset-0 z-50"
>
    <div class="absolute inset-0 bg-black/50"></div>

    <div
        class="absolute right-0 top-0
               h-full w-full max-w-md
               bg-white p-6 shadow-xl"
        role="dialog"
        aria-modal="true"
        aria-label="Selezione regalo"
    >

        <div class="flex justify-between items-center">

            <h2 class="text-xl font-semibold">
                Scegli il tuo regalo
            </h2>

            <button
                type="button"
                @click="$store.promotion.closeDrawer()"
                aria-label="Chiudi"
            >
                Chiudi
            </button>

        </div>

        <div class="mt-6">
            <!-- Elenco dei regali disponibili -->
        </div>

    </div>
</div>

Il drawer viene aperto chiamando:

Alpine.store('promotion').openDrawer();

Oppure da qualsiasi componente Alpine:

<button
    @click="$store.promotion.openDrawer()"
>
    Scegli il regalo
</button>

Il vantaggio è evidente: il componente che apre il drawer non deve conoscere la sua posizione nel DOM.

Questo riduce l’accoppiamento tra i template.

In un’implementazione production-ready è necessario completare il drawer con gestione del focus, ripristino del focus alla chiusura e blocco dello scroll, oltre a collegare la selezione del regalo a un endpoint Magento.

8. Sincronizzare Alpine.store con il carrello Magento 2

Finora abbiamo gestito uno stato JavaScript indipendente.

In un progetto reale, però, il subtotale deve essere aggiornato quando cambia il carrello Magento.

Hyvä espone eventi frontend utilizzabili per sincronizzare i componenti.

Uno degli eventi interessanti è:

update-totals

Questo evento viene utilizzato nella gestione dei totali del carrello Hyvä.

Possiamo intercettarlo e aggiornare lo store.

window.addEventListener('update-totals', (event) => {

    const totalsData = event.detail?.data;

    if (!totalsData) {
        return;
    }

    const subtotalSegment =
        totalsData.total_segments?.find(
            segment => segment.code === 'subtotal'
        );

    if (!subtotalSegment) {
        return;
    }

    Alpine.store('promotion').updateCartTotal(
        subtotalSegment.value
    );

});

Il flusso diventa:

Cliente modifica quantità
          |
          v
Magento aggiorna carrello
          |
          v
Hyvä aggiorna i totali
          |
          v
Evento update-totals
          |
          v
Alpine.store('promotion')
          |
          v
Aggiornamento automatico
dei componenti frontend

Attenzione al subtotale utilizzato

Magento può esporre differenti valori economici:

  • Subtotale prima degli sconti.
  • Subtotale dopo gli sconti.
  • Totale comprensivo di imposte.
  • Totale escluso imposte.
  • Grand total.

La scelta dipende dalle regole della promozione.

Per esempio, se il regalo viene assegnato quando il cliente spende almeno 100 euro dopo gli sconti, utilizzare il subtotale lordo può produrre risultati errati.

La regola deve essere coerente con la validazione backend.

Inoltre, l’evento update-totals non rappresenta necessariamente un meccanismo universale per ogni pagina Magento. Per i componenti presenti fuori dal carrello può essere necessario sincronizzare anche i dati delle sezioni private di Hyvä.

9. Un problema importante: la sostituzione dinamica del DOM in Hyvä

Un aspetto che merita particolare attenzione riguarda il ciclo di vita dei componenti.

In alcune operazioni, Hyvä può sostituire dinamicamente porzioni del DOM.

Per esempio, dopo una modifica al carrello può essere utilizzata una funzione come:

hyva.replaceDomElement()

Questo significa che determinati elementi HTML vengono rimossi e sostituiti.

Se un listener è registrato all’interno di un elemento che viene distrutto, anche il componente Alpine associato può cessare di funzionare.

Lo store globale, invece, continua a esistere.

Perché?

Perché Alpine.store() non è associato a un singolo nodo DOM.

Lo store rimane disponibile per tutta la durata della pagina.

Tuttavia, i listener collegati a componenti rimossi dal DOM devono essere registrati nuovamente, oppure collocati in un elemento persistente.

Strategia consigliata

Per le funzionalità condivise:

  1. Registrare lo store una sola volta.
  2. Mantenere i listener globali in una posizione persistente.
  3. Evitare di collocare i listener critici all’interno delle sezioni DOM sostituibili.
  4. Consentire ai nuovi componenti Alpine di leggere lo store esistente.

Per esempio, un listener dichiarativo può essere associato a un elemento persistente:

<div
    x-data
    @update-totals.window="
        $store.promotion.updateCartTotal(
            $event.detail.data.total_segments
                .find(segment => segment.code === 'subtotal')
                ?.value ?? 0
        )
    "
></div>

Questo elemento dovrebbe essere collocato fuori dalle aree sostituite dinamicamente.

Nel carrello Hyvä, un punto importante da considerare è #maincontent.

Collocare un listener persistente fuori da quest’area evita che venga eliminato durante alcune operazioni di aggiornamento del carrello.

Naturalmente, il comportamento va verificato rispetto alla versione di Hyvä utilizzata e alle personalizzazioni presenti nel progetto.

10. Integrare lo store in un modulo Magento 2

In un progetto strutturato non è consigliabile duplicare lo script di inizializzazione nei vari template.

Una possibile organizzazione è:

app/code/Devlogica/Promotion/
│
├── registration.php
├── etc/
│   └── module.xml
│
└── view/
    └── frontend/
        ├── layout/
        │   └── default.xml
        │
        ├── templates/
        │   ├── promotion-store.phtml
        │   ├── promotion-banner.phtml
        │   ├── promotion-progress.phtml
        │   └── promotion-drawer.phtml
        │
        └── web/

Lo store viene inizializzato attraverso un template dedicato.

Gli altri template contengono esclusivamente la logica di presentazione.

Esempio di registrazione nel layout XML

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">

    <body>
        <referenceContainer name="before.body.end">

            <block
                name="devlogica.promotion.store"
                template="Devlogica_Promotion::promotion-store.phtml"
            />

        </referenceContainer>
    </body>
</page>

Il nome del container e la posizione effettiva devono essere verificati rispetto al layout del tema utilizzato.

L’obiettivo è registrare lo store una sola volta e renderlo disponibile ai componenti che ne hanno bisogno.

Compatibilità con Content Security Policy

Nelle versioni moderne di Hyvä è importante considerare anche le regole CSP.

Quando utilizziamo JavaScript inline in un template .phtml, dobbiamo adottare il meccanismo di registrazione previsto dalla versione di Hyvä installata.

Un esempio, nei progetti che supportano HyvaCsp, è:

<?php

/** @var \Hyva\Theme\ViewModel\HyvaCsp $hyvaCsp */

?>

<script>
document.addEventListener('alpine:init', () => {

    if (Alpine.store('promotion')) {
        return;
    }

    Alpine.store('promotion', {
        threshold: 100,
        cartTotal: 0
    });

});
</script>

<?php $hyvaCsp->registerInlineScript(); ?>

La registrazione CSP deve essere verificata in funzione della versione del tema e della configurazione del progetto.

11. Alpine.store oppure eventi JavaScript?

Una domanda importante riguarda la scelta tra uno store globale e gli eventi personalizzati.

Hyvä utilizza frequentemente eventi JavaScript per comunicare tra componenti.

Per esempio:

window.dispatchEvent(
    new CustomEvent('promotion-updated', {
        detail: {
            cartTotal: 120
        }
    })
);

Un componente può intercettare l’evento:

<div
    x-data="{ total: 0 }"
    @promotion-updated.window="
        total = $event.detail.cartTotal
    "
>
    <span x-text="total"></span>
</div>

Questo approccio è perfettamente valido.

Tuttavia, esiste una differenza architetturale.

Gli eventi comunicano che qualcosa è accaduto. Gli store rappresentano lo stato corrente dell’applicazione.

Consideriamo due casi.

Caso A: notifica di un’operazione

Un prodotto è stato aggiunto al carrello.

window.dispatchEvent(
    new CustomEvent('product-added')
);

In questo scenario un evento è appropriato.

Caso B: stato condiviso

Cinque componenti devono sapere se il cliente ha raggiunto la soglia per ricevere un regalo.

In questo caso uno store globale può essere più semplice.

Alpine.store('promotion').isEligible

Non dobbiamo propagare manualmente il valore a ogni componente.

Quando scegliere una soluzione

Scenario Soluzione consigliata
Apertura di un menu locale x-data
Stato di una singola modale x-data
Notifica di aggiunta al carrello CustomEvent
Stato condiviso di una promozione Alpine.store
Preferenze UI condivise Alpine.store
Comunicazione con API Magento Fetch API + stato Alpine
Validazione delle regole commerciali Backend Magento

Le due tecniche non sono alternative assolute.

In molte applicazioni Hyvä, la soluzione migliore consiste nell’utilizzare eventi per ricevere notifiche e Alpine.store per mantenere uno stato condiviso.

12. Errori comuni nell’utilizzo di Alpine.store con Magento 2

12.1 Utilizzare lo store per qualsiasi componente

Non ogni stato deve essere globale.

Un semplice accordion può essere implementato così:

<div x-data="{ open: false }">

    <button @click="open = !open">
        Dettagli prodotto
    </button>

    <div x-show="open">
        Informazioni aggiuntive
    </div>

</div>

Creare uno store globale per questa funzionalità introdurrebbe complessità inutile.

12.2 Registrare più volte lo stesso store

Se più template inizializzano lo stesso store, si rischia di sovrascrivere lo stato.

Utilizzare una registrazione centralizzata e un controllo preventivo:

if (!Alpine.store('promotion')) {
    Alpine.store('promotion', {
        cartTotal: 0
    });
}

12.3 Considerare lo store una fonte dati persistente

Uno store Alpine.js esiste nella memoria JavaScript della pagina.

Quando il browser esegue una nuova navigazione completa, lo store viene ricreato.

Non sostituisce:

  • Il database Magento.
  • La sessione cliente.
  • Il carrello persistente.
  • Le API backend.

Lo store è uno strumento per gestire lo stato frontend.

12.4 Salvare informazioni sensibili nello store

Le proprietà di uno store sono accessibili dal codice JavaScript della pagina.

Non dobbiamo utilizzare Alpine.store per memorizzare:

  • Password.
  • Token riservati.
  • Credenziali API.
  • Informazioni sensibili non destinate al browser.

12.5 Utilizzare lo stato frontend per validare le promozioni

Nel nostro esempio:

get isEligible() {
    return this.cartTotal >= this.threshold;
}

La proprietà controlla esclusivamente il comportamento dell’interfaccia.

Un utente potrebbe modificare il valore dello store tramite gli strumenti di sviluppo del browser.

Pertanto, Magento deve verificare nuovamente:

  • Il subtotale effettivo.
  • La validità della promozione.
  • L’idoneità del prodotto omaggio.
  • Eventuali limiti di utilizzo.
  • La disponibilità del prodotto.

Le regole commerciali devono essere applicate lato server.

13. Performance: Alpine.store migliora le prestazioni?

L’utilizzo di Alpine.store non rende automaticamente un sito Magento più veloce.

Il vantaggio principale riguarda la semplicità della gestione dello stato.

Tuttavia, una buona architettura può contribuire indirettamente alle performance frontend.

Per esempio, centralizzare uno stato condiviso può evitare:

  • Duplicazione della logica JavaScript.
  • Listener ridondanti.
  • Sincronizzazioni manuali tra componenti.
  • Aggiornamenti DOM non necessari.
  • Implementazioni ripetute della stessa funzionalità.

Bisogna comunque evitare store eccessivamente grandi.

Uno store con centinaia di proprietà, molte dipendenze e aggiornamenti frequenti può diventare difficile da mantenere.

Separare gli store per responsabilità

Una possibile architettura è:

Alpine.store('promotion', {
    // Stato promozioni
});

Alpine.store('wishlistUi', {
    // Stato interfaccia wishlist
});

Alpine.store('navigation', {
    // Stato navigazione
});

È preferibile rispetto a uno store monolitico che gestisce ogni funzionalità del frontend.

Anche la denominazione degli store è importante.

In un progetto Magento con molti moduli di terze parti, utilizzare nomi sufficientemente specifici riduce il rischio di collisioni.

14. Testing e debugging degli store Alpine.js

Durante lo sviluppo possiamo ispezionare direttamente lo stato dalla console del browser.

Per esempio:

Alpine.store('promotion')

Per verificare la soglia:

Alpine.store('promotion').threshold

Per modificare il subtotale:

Alpine.store('promotion').updateCartTotal(120)

Per verificare l’idoneità:

Alpine.store('promotion').isEligible

Il risultato dovrebbe essere:

true

Possiamo anche verificare il comportamento della progress bar:

Alpine.store('promotion').progress

Risultato:

100

Test funzionali consigliati

Prima di distribuire la funzionalità in produzione, è opportuno verificare almeno questi scenari:

Test Risultato atteso
Carrello sotto la soglia Promozione non disponibile
Carrello raggiunge la soglia Promozione disponibile
Carrello supera la soglia Progress bar al 100%
Carrello scende sotto la soglia Promozione disabilitata
Apertura drawer Drawer visibile
Chiusura drawer Drawer nascosto
Aggiornamento dinamico carrello Store sincronizzato
Nuova navigazione completa Stato ricostruito dai dati correnti
Aggiornamento della pagina Nessuna duplicazione dei listener

Per un progetto Magento complesso, questi scenari possono essere automatizzati con Playwright.

È particolarmente importante verificare che le operazioni di aggiornamento del carrello non provochino la perdita dei listener e che lo stato frontend rimanga coerente con quello del backend.

15. Best practice per Alpine.store nei progetti Hyvä

Quando sviluppiamo funzionalità custom per Magento 2 con Hyvä, consigliamo di seguire alcune regole architetturali.

1. Mantenere locale lo stato che non deve essere condiviso.

Utilizzare x-data per componenti indipendenti.

2. Centralizzare soltanto lo stato realmente globale.

Utilizzare Alpine.store() quando più componenti devono leggere o modificare le stesse informazioni.

3. Separare la logica di business dalla presentazione.

I template .phtml dovrebbero contenere principalmente markup e binding Alpine.

4. Evitare duplicazioni dello stato.

Se un valore può essere calcolato attraverso un getter, non è necessario mantenerne una seconda copia.

5. Gestire correttamente il ciclo di vita del DOM.

Prestare attenzione alle sostituzioni dinamiche effettuate da Hyvä.

6. Considerare sempre la sicurezza.

Le verifiche commerciali e le operazioni sensibili devono essere validate dal backend Magento.

7. Verificare la compatibilità con CSP.

Gli script inline devono rispettare le regole di sicurezza della versione di Hyvä utilizzata.

8. Testare i componenti in modo integrato.

Non basta verificare il singolo componente: è necessario controllare la sincronizzazione tra store, eventi frontend e API Magento.

Conclusioni

Alpine.store() è uno strumento molto utile per sviluppare funzionalità frontend avanzate in Magento 2 con Hyvä Themes.

Permette di condividere uno stato reattivo tra componenti indipendenti, evitando di costruire sistemi complessi di sincronizzazione basati esclusivamente su eventi JavaScript.

I principali vantaggi sono:

  • Stato condiviso tra template Magento differenti.
  • Aggiornamento automatico dei componenti.
  • Riduzione dell’accoppiamento tra blocchi frontend.
  • Codice più semplice da mantenere.
  • Maggiore riutilizzabilità delle funzionalità.
  • Integrazione naturale con Alpine.js e Hyvä.

La scelta tra x-data, eventi personalizzati e Alpine.store() deve comunque dipendere dalle responsabilità del componente.

Una buona architettura frontend non consiste nel rendere globale tutto lo stato, ma nel condividere soltanto quello che deve realmente essere condiviso.

Nei progetti Magento 2 con numerose personalizzazioni, questa distinzione può fare la differenza tra un frontend semplice da evolvere e un sistema difficile da mantenere.

Sviluppo Magento 2 e Hyvä con Devlogica

In Devlogica ci occupiamo di sviluppo Magento 2, personalizzazione di moduli, integrazione con sistemi esterni e realizzazione di frontend basati su Hyvä Themes.

Lavoriamo su architetture frontend e backend con particolare attenzione a performance, manutenibilità e compatibilità con l’ecosistema Magento.

Se stai sviluppando un e-commerce Magento 2 e vuoi implementare funzionalità avanzate con Hyvä e Alpine.js, possiamo aiutarti ad analizzare l’architettura esistente e progettare una soluzione adatta alle esigenze del progetto.

Contattaci per una consulenza tecnica su Magento 2 e Hyvä.

Riferimenti tecnici

L’articolo approfondisce il tema trattato da Anastasiia Bondar in Alpine.store in the Hyvä theme, pubblicato il 23 aprile 2026.

Per ulteriori approfondimenti:

  • Alpine.js — documentazione ufficiale di Alpine.store().
  • Alpine.js — documentazione ufficiale di $store.
  • Hyvä Themes — documentazione tecnica sul frontend Magento.
  • Anastasiia Bondar — Alpine.store in the Hyvä theme.