Turn free-typed field/object names into dropdowns in the Répéteur and Donnée liée filters

Two settings previously required typing an exact field or object name by hand:

- "champ" (Répéteur's filtre_champ/filtre2_champ, Donnée liée's data_filtre_champ/data_filtre2_champ) is now a dropdown of the actual fields on the object already chosen for that widget — pre-populated server-side (controls_with_values.py) and live-updated client-side without a reload when you change the object (bindDefinitionFieldSelects(), generalizing the existing Jauge champ/row_id pattern).

- "valeur" (the comparison value, which could already reference another object's field via a "{{Objet.champ}}" string typed by hand) is now a "Valeur fixe" / "Donnée d'un autre objet" toggle — the dynamic mode shows an object dropdown and a dependent field dropdown, and picking from them reconstructs that internal reference string automatically (initFilterValuePickers()/updateFilterValueFromDynamic()). The underlying stored value and _resolve_filter_value's parsing are unchanged, so existing elements using the old typed syntax still load correctly and populate the pickers on open.

Guards against writing a literal "{{...}}" pair directly in the Jinja template source (caught by a real render error while building this — Jinja parses {{/}} anywhere in the file, including inside comments) by building that syntax from split string literals in JS instead.

Verified end to end: repeater panel renders the pickers, saving with a dynamic reference filters correctly against live data (current_level changing which row shows), and reloading pre-selects the right object/field in the dropdowns. Full suite green (89).
This commit is contained in:
william
2026-08-25 06:32:21 +02:00
parent 33a887e7ba
commit 343732d7aa
4 changed files with 160 additions and 17 deletions
+9 -1
View File
@@ -21,6 +21,12 @@ def controls_with_values(slug, el):
# dans screen_edit.html), ceci ne couvre que le premier affichage/une
# navigation vers un élément déjà réglé.
jauge_options = data_definition_options(slug, attributes.get("_definition_id"))
# Le Texte/Titre lié à une donnée (voir DATA_BINDING_CONTROLS) garde SON
# objet dans un attribut séparé (_data_definition_id) — jamais le même
# élément que le Répéteur/la Jauge, donc jamais besoin des deux options
# en même temps, mais un calcul dédié quand même pour rester correct si
# ça change un jour.
data_binding_options = data_definition_options(slug, attributes.get("_data_definition_id"))
result = []
for control in meta.get("controls", []):
control = dict(control)
@@ -32,9 +38,11 @@ def controls_with_values(slug, el):
control["options"] = [("", "(aucun — utiliser le modèle de texte ci-dessous)")] + [(str(t["id"]), t["name"]) for t in types]
if control["key"] == "row_id":
control["options"] = [("", "(sélectionner un objet d'abord)")] + [(str(r["id"]), r["label"]) for r in jauge_options["rows"]]
if control["key"] == "champ":
if control["key"] in ("champ", "filtre_champ", "filtre2_champ"):
control["options"] = [("", "(sélectionner un objet d'abord)")] + [(f["name"], f["name"]) for f in jauge_options["fields"]]
if control["key"] == "champ_nom":
control["options"] = [("", "— aucun —")] + [(f["name"], f["name"]) for f in jauge_options["fields"]]
if control["key"] in ("data_filtre_champ", "data_filtre2_champ"):
control["options"] = [("", "(sélectionner un objet d'abord)")] + [(f["name"], f["name"]) for f in data_binding_options["fields"]]
result.append({**control, "value": _control_value(el, control)})
return result