> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aftersell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Hooks

> Modifiez le comportement du panier Aftersell : transformez des lignes, enrichissez-les avec des données Storefront, façonnez les options d'abonnement et contrôlez l'ajout au panier.

Là où les [événements](/fr/aftersell/cart/sdk-events) vous permettent de *réagir* au panier et les [actions](/fr/aftersell/cart/sdk-actions) de le *modifier*, les **hooks** changent le comportement du panier lui-même : comment les lignes s'affichent, quelles données elles portent et ce qui se passe à l'ajout au panier.

Les hooks se trouvent sous `window.aftersell.cart.hooks`.

<Note>
  Un hook change ce que l'acheteur **voit** ; une action change ce qui est **dans son panier**. Masquer une ligne de cadeau gratuit avec une transformation la laisse dans le panier et dans le total. La retirer avec [`removeItem`](/fr/aftersell/cart/sdk-actions#removeitemkey) la supprime pour de bon.
</Note>

<Note>
  Les hooks sont des appels de configuration, il est donc sûr de les enregistrer tout en haut de votre script, sans besoin d'attendre `ready()`. Enregistrez-les dans le script **Initialization** de votre panier (voir [Scripts personnalisés](/fr/aftersell/cart/custom-scripts)).
</Note>

<div id="how-registration-works">
  ## Comment fonctionne l'enregistrement
</div>

Chaque hook est une méthode `register*`. Vous l'appelez avec votre fonction ; elle renvoie une **fonction de désenregistrement** que vous pouvez appeler pour retirer la vôtre.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const off = window.aftersell.cart.hooks.registerSkipAddToCartRule((form) =>
  form.hasAttribute('data-skip-aftersell')
);

// later: off();
```

L'enregistrement est **additif**, donc votre fonction s'exécute aux côtés de toutes les autres. C'est important, car votre script est rarement le seul sur la page : une application d'abonnement, une application de bundle et le thème lui-même peuvent tous s'enregistrer sur le même hook. Aucun d'eux ne peut remplacer le vôtre, et rien de ce que vous enregistrez ne peut être silencieusement écarté par ce qui se charge après vous.

| Hook                                                                                      | Ce qu'il fait                                                                                                                                         | Avec plusieurs enregistrements                                    |
| ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| [`registerLineTransform`](#registerlinetransform)                                         | Masquer ou renommer des lignes individuelles.                                                                                                         | Tous s'exécutent, dans l'ordre d'enregistrement.                  |
| [`registerLineComparator`](#registerlinecomparator)                                       | Réordonner les lignes affichées.                                                                                                                      | Se composent comme départageurs.                                  |
| [`registerCartEnricher`](#registercartenricher)                                           | Attacher des données Storefront supplémentaires à chaque ligne.                                                                                       | Tous s'exécutent ; chaque `id` est son propre espace de noms.     |
| [`registerSubscriptionOptionsTransform`](#registersubscriptionoptionstransform)           | Masquer ou renommer les plans de vente d'une ligne.                                                                                                   | Tous s'exécutent ; les correctifs fusionnent par plan, par champ. |
| [`registerDefaultSubscriptionOptionSelector`](#registerdefaultsubscriptionoptionselector) | Choisir quel plan est présélectionné.                                                                                                                 | La première réponse non-`null` l'emporte.                         |
| [`registerSkipAddToCartRule`](#registerskipaddtocartrule)                                 | Laisser des formulaires spécifiques contourner le panier. Consultez [Interception de l'ajout au panier](/fr/aftersell/cart/add-to-cart-interception). | Toute règle renvoyant `true` fait passer outre.                   |

Un hook qui lève une exception, ou qui n'est pas une fonction, est ignoré ; les autres s'exécutent quand même, et le panier continue. Une intégration défaillante ne peut pas faire tomber l'ajout au panier, le sélecteur d'abonnement ou le tri.

Le revers de la médaille est qu'un hook défaillant de votre part échoue **silencieusement** : rien n'atteint la console du navigateur. Consultez [Débogage](/fr/aftersell/cart/sdk-overview#debugging) pour savoir où ces échecs apparaissent réellement.

***

<div id="registerlinetransform">
  ## registerLineTransform
</div>

`registerLineTransform(fn)` s'exécute pour chaque ligne du panier avant son rendu. Utilisez-le pour masquer une ligne ou changer sa présentation, sans toucher à ce qui est réellement dans le panier de l'acheteur.

La fonction reçoit une ligne en lecture seule plus des setters. Elle renvoie une fonction de désenregistrement.

| Setter                            | Effet                                                                                                                                                                    |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `setHidden(bool)`                 | Masquer la ligne du tiroir. Elle reste dans le panier et dans le total.                                                                                                  |
| `setTitle(string)`                | Changer le titre affiché.                                                                                                                                                |
| `setVariantTitle(string \| null)` | Changer le libellé de variante affiché.                                                                                                                                  |
| `setInternalProperties(obj)`      | Fusionner des propriétés d'affichage uniquement. Jamais persistées vers Shopify. Utilisé pour [regrouper les lignes de bundle](/fr/aftersell/cart/sdk-use-case-bundles). |

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Hide free gift lines from the drawer. The cart total is unaffected.
const off = window.aftersell.cart.hooks.registerLineTransform((line) => {
  if (line.finalLinePrice === 0) {
    line.setHidden(true);
  }
  if (line.sellingPlan) {
    line.setVariantTitle(`Delivered ${line.sellingPlan.name.toLowerCase()}`);
  }
});

// later: off();
```

<Warning>
  Une transformation ne change que ce qui s'affiche. Elle ne peut pas changer le prix, la quantité ou l'identité de la ligne. Utilisez les [actions](/fr/aftersell/cart/sdk-actions) pour cela.
</Warning>

**À utiliser pour :** masquer les lignes de cadeau avec achat ou injectées par des applications, renommer les lignes d'abonnement, marquer les articles en réduction, masquer les composants de bundle que l'acheteur ne devrait pas gérer individuellement.

`setInternalProperties` est le setter derrière le regroupement de bundles : apposer les propriétés canoniques du bundle sur chaque ligne est la façon de faire s'afficher les lignes de panier séparées d'une application tierce comme un seul article. Consultez [Regrouper les lignes de bundle d'une autre application](/fr/aftersell/cart/sdk-use-case-bundles).

<div id="registerlinecomparator">
  ## registerLineComparator
</div>

Un comparateur de la même forme que celle attendue par `Array.prototype.sort`. Il s'exécute après le masquage et le renommage, donc il voit les lignes transformées.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Subscriptions first, then everything else.
window.aftersell.cart.hooks.registerLineComparator((lineA, lineB) => {
  return (lineB.sellingPlan ? 1 : 0) - (lineA.sellingPlan ? 1 : 0);
});
```

Les comparateurs **se composent comme départageurs** : le premier à renvoyer une valeur non nulle décide pour cette paire, et les autres ne sont consultés qu'en cas d'égalité. Renvoyez `0` pour les paires sur lesquelles vous n'avez pas d'avis. C'est ce qui transmet la décision au comparateur suivant au lieu de lui imposer un ordre.

**À utiliser pour :** faire remonter les abonnements ou les articles à forte valeur en haut, faire descendre les cadeaux gratuits et les options complémentaires en bas, garder un produit sponsorisé en premier.

<div id="registercartenricher">
  ## registerCartEnricher
</div>

`registerCartEnricher(registration)` récupère des données supplémentaires de produit ou de variante depuis l'API Storefront de Shopify et les attache à chaque ligne de panier correspondante dans `line.metadata[id]`. Utilisez-le pour faire apparaître des metafields, des tags ou tout autre élément exposé par l'API Storefront, sans aucun changement de code requis de la part d'Aftersell.

| Champ      | Type                              | Description                                                                                                                                  |
| ---------- | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`       | `string`                          | Espace de noms du résultat ; il atterrit dans `line.metadata[id]`. Doit être unique ; un second enregistrement avec le même `id` est ignoré. |
| `onType`   | `'Product'` ou `'ProductVariant'` | Le nœud ciblé par le fragment. C'est aussi la clé de jointure (ID de produit vs ID de variante).                                             |
| `fragment` | `string`                          | Une sélection de champs GraphQL (sans accolades externes) insérée dans la requête Storefront. Les accolades doivent être équilibrées.        |

Renvoie une **fonction de désenregistrement**.

Chaque fois que le panier se charge ou change, Aftersell récupère votre fragment pour chaque produit ou variante du panier et attache le résultat. La récupération est non bloquante : le panier s'affiche immédiatement et réémet `cart_updated` une fois les données arrivées. Un fragment lent ou défaillant ne retarde ni ne casse jamais le panier.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerCartEnricher({
  id: 'pricing',
  onType: 'ProductVariant',
  fragment: `
    anchorPrice: metafield(namespace: "custom", key: "anchor_price") { value }
    subscriberPrice: metafield(namespace: "custom", key: "subscriber_price") { value }
  `,
});

// Read it once the data arrives.
window.aftersell.cart.events.on('cart_updated', (state) => {
  state.items.forEach((line) => {
    const anchor = line.metadata.pricing?.anchorPrice;
    if (anchor) console.log(line.title, 'anchor price', anchor.value);
  });
});
```

Comme l'enrichissement est asynchrone, protégez toujours la lecture, car `line.metadata.pricing` est `undefined` jusqu'à ce que la première récupération se résolve, et `metadata` lui-même vaut `{}` par défaut.

**À utiliser pour :** tirer un metafield sur chaque ligne (une estimation de livraison, une liste d'ingrédients, un indicateur « expédié séparément », un multiplicateur de fidélité) et l'afficher via un [bloc Custom code](/fr/aftersell/cart/custom-code-blocks). Consultez [afficher les données de metafields sur les lignes du panier](/fr/aftersell/cart/sdk-use-case-metafields).

<Note>
  Plusieurs enrichisseurs coexistent sans problème, puisque chaque `id` est son propre espace de noms, leurs données n'entrent donc jamais en collision.
</Note>

<Warning>
  Les valeurs enrichies sont renvoyées telles quelles depuis l'API Storefront et ne sont **pas** assainies. Affichez-les en tant que texte, pas en tant que HTML brut.
</Warning>

<div id="registersubscriptionoptionstransform">
  ## registerSubscriptionOptionsTransform
</div>

Masquez ou renommez les plans de vente proposés sur une ligne. Votre fonction reçoit des options en lecture seule plus des setters, et ne renvoie rien.

| Setter            | Effet                           |
| ----------------- | ------------------------------- |
| `setHidden(bool)` | Masquer le plan du sélecteur.   |
| `setName(string)` | Changer le nom de plan affiché. |

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerSubscriptionOptionsTransform((options, context) => {
  // context: { productId, variantId }
  options.forEach((option) => {
    if (option.discountPercent === 0) option.setHidden(true);
    option.setName(option.name.replace('Every ', ''));
  });
});
```

**Des setters, et non une liste renvoyée, afin que plusieurs scripts puissent coexister.** Si ce hook renvoyait un tableau, une transformation ne s'intéressant qu'à un seul plan écrirait naturellement `options.filter(...)` et supprimerait silencieusement au passage les plans de toutes les autres applications. Avec les setters, vous ne pouvez décrire que vos propres modifications : les correctifs fusionnent par plan et par champ, et le dernier écrivain l'emporte lors d'un véritable conflit sur le même champ du même plan. Une transformation qui lève une exception ne contribue à rien, et les autres s'appliquent quand même.

Chaque transformation voit les options *originales*, pas une vue à moitié corrigée, donc l'ordre d'enregistrement ne change pas ce que vous lisez.

<Note>
  L'ordre des plans reste tel que Shopify l'a renvoyé, donc une transformation ne peut pas réordonner. Pour contrôler quel plan est proposé en premier (et auquel le bouton de passage à l'abonnement souscrit), utilisez [`registerDefaultSubscriptionOptionSelector`](#registerdefaultsubscriptionoptionselector), qui fait remonter son choix en tête.
</Note>

Vous ne pouvez pas non plus *ajouter* un plan ni changer un prix : `discountPercent` n'a pas de setter, car un plan que Shopify n'honorera pas au paiement ne serait qu'une promesse brisée dans le sélecteur.

<div id="registerdefaultsubscriptionoptionselector">
  ## registerDefaultSubscriptionOptionSelector
</div>

Choisissez quel plan est présélectionné sur une ligne. Renvoyez un `id` de plan, ou `null` pour passer votre tour.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerDefaultSubscriptionOptionSelector((options) => {
  const best = options
    .slice()
    .sort((optionA, optionB) => optionB.discountPercent - optionA.discountPercent)[0];
  return best ? best.id : null;
});
```

Le **premier sélecteur à renvoyer l'id d'un plan disponible l'emporte**, donc renvoyez `null` pour les lignes qui ne vous intéressent pas plutôt que de deviner. Cela transmet la décision au sélecteur suivant au lieu de la court-circuiter. Un id qui ne correspond à aucun plan de la ligne est traité comme `null` et s'efface également, donc un id obsolète ne peut pas vider le sélecteur.

Votre fonction reçoit `(options, context)`, le même `context` que reçoit la transformation d'options.

<div id="registerskipaddtocartrule">
  ## registerSkipAddToCartRule
</div>

Renvoyez `true` pour laisser un formulaire produit spécifique ajouter au panier normalement, en contournant entièrement Aftersell. C'est utile pour un formulaire qui a besoin de sa propre redirection ou gestion.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerSkipAddToCartRule((form) =>
  form.hasAttribute('data-skip-aftersell')
);
```

**Tout `true` fait passer outre**, donc gardez votre règle étroite, correspondant aux formulaires spécifiques qui vous appartiennent, et renvoyez `false` pour tout le reste. Les règles sont évaluées dans l'ordre d'enregistrement et s'arrêtent au premier `true`, donc n'y mettez pas d'effets de bord : le fait que la vôtre s'exécute ou non dépend de ce qui s'est enregistré avant elle.

<Tip>
  Si vous contrôlez le balisage du formulaire, vous n'avez pas du tout besoin d'un hook : ajoutez la classe **`aftersell-cart-skip-atc`** au `<form>` et Aftersell le laisse tranquille. Utilisez ce hook lorsque vous ne pouvez pas modifier le balisage, ou lorsque la décision dépend de quelque chose que seul votre code connaît.
</Tip>

**À utiliser pour :** un formulaire de précommande ou de devis qui a besoin de sa propre redirection, le flux personnalisé d'une application d'abonnement, un bouton « acheter maintenant » qui doit aller directement au paiement. Pour désactiver plutôt l'interception pour toute la page, utilisez [`skip_add_to_cart_interceptor`](/fr/aftersell/cart/sdk-configure#skip_add_to_cart_interceptor), mais préférez ce hook, qui est limité aux formulaires que vous nommez.

<div id="where-to-go-next">
  ## Pour aller plus loin
</div>

* **[Objet cart](/fr/aftersell/cart/sdk-cart-object)** : la forme de la ligne que reçoit une transformation.
* **[Événements](/fr/aftersell/cart/sdk-events)** : tout ce à quoi vous pouvez vous abonner.
* **[Actions](/fr/aftersell/cart/sdk-actions)** : lire et modifier le panier.
* **[Cas d'usage](/fr/aftersell/cart/sdk-use-cases)** : des solutions complètes aux demandes courantes.
