From 666aa892e0b8d7e9057c14f0e731c3d883d969a3 Mon Sep 17 00:00:00 2001 From: william Date: Sat, 29 Aug 2026 20:23:11 +0200 Subject: [PATCH] =?UTF-8?q?Corrige=20le=20ciblage=20=C3=A9l=C3=A9ment+lign?= =?UTF-8?q?e=20transmis=20par=20un=20=C3=A9v=C3=A9nement=20dans=20un=20R?= =?UTF-8?q?=C3=A9p=C3=A9teur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signalé par l'utilisateur : quand "Modifier un élément" utilise la case "Utiliser l'élément transmis par l'événement en cours" (target_element_from_event), et que cet élément vit à l'intérieur d'un Répéteur, la résolution ne ciblait jamais que le PREMIER élément correspondant trouvé dans toute la page — jamais forcément la bonne ligne. Vérifié directement sur un rendu réel : chaque ligne d'un Répéteur rejoue le MÊME modèle (voir render_repeater.py), donc le MÊME data-element-id se répète à l'identique sur CHAQUE .repeaterItem — seul data-row-id (posé sur l'enveloppe .repeaterItem) distingue réellement une ligne d'une autre. Un simple document.querySelector('[data-element-id]') global tombe donc toujours sur la première ligne rencontrée dans le DOM, sans rapport avec la ligne réellement transmise par l'événement (window.lastEventParams.row_id). Corrigé : quand l'événement transmet aussi une ligne, la recherche est désormais scopée à l'intérieur du .repeaterItem[data-row-id=...] correspondant avant d'y chercher l'élément — sinon (élément fixe, ou événement sans paramètre de ligne), le comportement global d'avant reste inchangé. 210 tests toujours verts (ce correctif est purement côté client, jamais couvert par les tests automatisés — vérifié manuellement via un rendu réel confirmant la structure .repeaterItem/data-row-id décrite ci-dessus). Co-Authored-By: Claude Sonnet 5 --- templates/play.html | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/templates/play.html b/templates/play.html index 5d14cb78..fb68b1be 100644 --- a/templates/play.html +++ b/templates/play.html @@ -796,10 +796,32 @@ // l'éditeur — contourne la contrainte de clé étrangère de // target_element_id, qui empêche d'y stocker un sentinel comme // EVENT_ROW_ID directement (voir screens/flow/ensure_flow_schema.py). + // + // data-element-id N'EST PAS unique quand l'élément vit dans un + // Répéteur : chaque ligne rejoue le MÊME modèle, donc le MÊME + // data-element-id se répète à l'identique sur chaque ligne (voir + // render_repeater.py, seul data-row-id — posé sur .repeaterItem, + // l'englobant — distingue réellement une ligne d'une autre). Un + // simple document.querySelector('[data-element-id]') global + // prendrait toujours la PREMIÈRE ligne, jamais la bonne — sans + // rapport avec l'événement reçu. Quand l'événement transmet aussi + // une ligne (row_id), la recherche est donc scopée à L'INTÉRIEUR de + // ce .repeaterItem précis ; sinon (élément fixe, ou événement sans + // paramètre de ligne), recherche globale comme avant. const resolvedElementId = node.target_element_from_event ? (window.lastEventParams ? window.lastEventParams.element_id : null) : node.target_element_id; - const targetEl = resolvedElementId ? document.querySelector('[data-element-id="' + resolvedElementId + '"]') : null; + const resolvedRowId = node.target_element_from_event && window.lastEventParams + ? window.lastEventParams.row_id : null; + let targetEl = null; + if (resolvedElementId != null) { + if (resolvedRowId != null) { + const rowEl = document.querySelector('.repeaterItem[data-row-id="' + resolvedRowId + '"]'); + targetEl = rowEl ? rowEl.querySelector('[data-element-id="' + resolvedElementId + '"]') : null; + } else { + targetEl = document.querySelector('[data-element-id="' + resolvedElementId + '"]'); + } + } if (targetEl) applyElementProperty(targetEl, node.element_property, node.element_value); return Promise.resolve(); } else if (node.action_type === 'declencher_evenement' && node.target_custom_event_id) {