Version de développement
Cette documentation décrit la version « next » (develop), non encore publiée. Pour la version stable, basculez sur « v5 » dans l'en-tête.
Personnaliser le rendu

Contrôleurs de formulaire

Découvrez comment enregistrer et lier des classes de contrôleur de formulaire à des types de documents spécifiques avec frappe.ui.form.set_controller.

Disponible depuis la version 5.x de Dodock

Introduction

Un contrôleur de formulaire est une classe JavaScript qui définit le comportement d'un type de document lors de la saisie et de l'affichage. Il s'agit de l'équivalent moderne et structuré des anciens scripts clients (Client Scripts), regroupant les gestionnaires d'événements (onload, validate, refresh, etc.) dans une classe réutilisable et extensible.

Les applications comme Dokos ou ERPNext lient traditionnellement ces contrôleurs en haut d'un script de formulaire. Dodock introduit désormais une API dédiée pour les enregistrer proprement et s'assurer qu'ils sont bien chargés au bon moment dans le cycle de vie du formulaire.

Pourquoi un nouveau mécanisme

Les limites de l'approche historique

Avant cette évolution, les applications liaient un contrôleur de classe au formulaire en l'étendant en haut du script de type de document :

extend_cscript(cur_frm.cscript, new erpnext.stock.DeliveryNoteController({ frm: cur_frm }));

Cette approche présentait deux inconvénients majeurs :

  • Pas de portée frm : le script s'exécute sans objet frm dans la portée, cur_frm est donc le seul moyen d'y accéder.
  • Gestionnaires d'événements ignorés : le branchement dans un gestionnaire setup ne fonctionne pas, car l'événement trigger("setup") collecte les gestionnaires à l'ancienne avant que ceux de la classe ne puissent être enregistrés. La méthode setup() du contrôleur n'est donc jamais déclenchée.

Une API explicite et fiable

La fonction frappe.ui.form.set_controller(doctype, ControllerClass) résout ces problèmes en enregistrant la classe auprès du gestionnaire de scripts. À l'initialisation du formulaire, après l'exécution des scripts de formulaire et des scripts clients, le ScriptManager branche automatiquement le contrôleur pour le type de document concerné, avant que l'événement setup ne soit déclenché.

Si un script a déjà lié une sous-classe du contrôleur — par exemple pour étendre un contrôleur d'application depuis un doctype_js personnalisé — ce branchement est conservé et prioritaire.

Enregistrer un contrôleur

Signature

frappe.ui.form.set_controller(doctype, ControllerClass)
  • doctype (chaîne) — le nom du type de document concerné (par exemple "Bon de livraison").
  • ControllerClass (classe) — la classe de contrôleur à lier à ce type de document.

Exemple

frappe.ui.form.set_controller("Delivery Note", erpnext.stock.DeliveryNoteController);

Cette instruction, placée dans le script de votre application, enregistre DeliveryNoteController comme contrôleur du type de document Delivery Note. Le gestionnaire de scripts se chargera de l'instancier et de le lier au formulaire au moment opportun.

Cycle de vie du branchement

L'ordre précis d'exécution garantit que les contrôleurs sont en place avant que les événements ne se déclenchent :

  1. Scripts de formulaire — les scripts déclarés sur le type de document s'exécutent.
  2. Scripts clients — les scripts clients configurés via l'interface s'appliquent.
  3. Branchement du contrôleur — le ScriptManager instancie et branche le contrôleur enregistré pour le type de document, à moins qu'une sous-classe n'ait déjà été liée par un script.
  4. Événement setup — l'événement setup est déclenché sur le formulaire. Les méthodes setup() du contrôleur sont désormais appelées.

Cette séquence permet aux développeurs de remplacer l'usage de cur_frm par frm dans la portée des méthodes du contrôleur, et garantit que les méthodes héritées fonctionnent comme prévu.

Héritage et extensions

Le mécanisme respecte l'héritage des contrôleurs. Si une application ou un script personnalisé étend un contrôleur existant :

class MonBonDeLivraisonPersonnalise extends erpnext.stock.DeliveryNoteController {
  refresh(frm) {
    super.refresh(frm);
    // Comportement supplémentaire
  }
}

Et le lie explicitement avant que le ScriptManager ne s'exécute, le branchement personnalisé est conservé. Le contrôleur de base enregistré via set_controller ne remplace pas cette sous-classe.

Types de documents concernés

Dodock utilise désormais set_controller pour lier le contrôleur de base DocTypeController sur deux types de documents techniques :

  • Type de document (DocType) — le formulaire de configuration des types de documents.
  • Formulaire personnalisé (Customize Form) — l'interface de personnalisation des formulaires.

Ces branchements internes illustrent l'adoption de l'API au cœur du framework et servent de référence pour les applications tierces.

Migration depuis l'ancienne approche

Si vous maintenez une application ou un script personnalisé qui utilise extend_cscript pour lier un contrôleur, vous pouvez migrer progressivement :

Avant :

extend_cscript(cur_frm.cscript, new erpnext.stock.DeliveryNoteController({ frm: cur_frm }));

Après :

frappe.ui.form.set_controller("Delivery Note", erpnext.stock.DeliveryNoteController);

La migration vous permet de :

  • Supprimer la dépendance à cur_frm dans le branchement.
  • Garantir que la méthode setup() du contrôleur est bien déclenchée.
  • Simplifier la lecture et la maintenance du code d'extension.

Bonnes pratiques

  • Un seul contrôleur par type de document — n'enregistrez qu'une seule classe par doctype. Pour étendre un contrôleur, créez une sous-classe et branchez-la explicitement si nécessaire.
  • Placez l'enregistrement au plus haut niveau — déclarez set_controller dans le script de formulaire de l'application, pas dans un gestionnaire d'événements.
  • Ne référencez pas cur_frm — utilisez l'argument frm passé aux méthodes du contrôleur. L'API est conçue pour rendre cur_frm superflu.
  • Testez les sous-classes — si votre application étend un contrôleur d'une autre application, vérifiez que le branchement de votre sous-classe survit bien au branchement automatique du contrôleur de base.

Voir aussi