Les événements et le POST

Un événement relie une action de l'utilisateur sur un bloc à une fonction 4D. Il est stocké dans le champ events de l'enregistrement du bloc :

"events": {
  "onClick": [
    { "vt_ClassName": "CONTACT", "vt_FunctionName": "togglePublish", "vo_Param": { } }
  ]
}

Un type d'événement contient une collection : plusieurs actions peuvent s'enchaîner, dans l'ordre.

Ce que peut être vt_ClassName

Le répartiteur accepte soit une dataclass, soit une classe projet :

If (ds[$vo_POST.vt_ClassName]#Null)
    $vo_Controller:=ds[$vo_POST.vt_ClassName]          // dataclass: used directly
Else
    $vo_Controller:=cs[$vo_POST.vt_ClassName].new()    // project class: INSTANTIATED
    $vo_Controller.vo_Session:=$vo_Param.vo_Session    // and handed the session
End if

Une classe contrôleur fonctionne donc, et elle reçoit même vo_Session, ce qu'une dataclass ne reçoit pas par ce chemin. Si un nom existe des deux côtés, ds l'emporte.

À savoir : la documentation écrite pour la V20 pose comme règle critique que vt_ClassName doit être une dataclass et ne peut pas être une classe projet. Ce n'est pas vrai en V21. Si une classe projet échoue sans message, la cause est sa visibilité (ci-dessous), pas la répartition.

Être appelable et être proposé dans le dev-panel sont deux choses différentes. BSPK_REFRESH_STORAGE construit la liste du sélecteur en parcourant les classes qui déclarent Class extends WebFormController, DataClass, Entity ou EntitySelection, et retient les fonctions dont la signature correspond à @(@)->$vo_WebResponse@. Une classe projet simple, sans extends, peut être appelée mais n'apparaîtra jamais dans le panneau : c'est ainsi qu'elle finit par sembler cassée.

Le suffixe WF : seulement pour les listes de valeurs

Il existe deux listes, construites avec deux motifs différents dans BSPK_REFRESH_STORAGE :

ListeMotif recherchéUtilisée parRègle de nommage
AvailableController@(@)->$vo_WebResponse@les événements (onClick, onChange…)aucune, tout nom convient
AvailableControllerForSelect@WF($vo_POST : Object)->$vo_Return : Object@vt_ValuesFunction de fieldSelect / fieldCheckboxdoit finir par WF

Pour un gestionnaire d'événement, c'est le type de retour qui compte, pas le nom. Pour une fonction qui alimente un menu déroulant ou un groupe de cases à cocher, le nom doit finir par WF et la signature doit correspondre au caractère près. Sinon le champ s'affiche vide, sans aucune erreur.

Après avoir ajouté une telle fonction : rechargez le code, puis lancez BSPK_REFRESH_STORAGE. Tant que ce n'est pas fait, le panneau ignore son existence.

Le nom de la fonction passe en camelCase avant l'appel

Le répartiteur appelle $vo_Controller[BSPK_String_To_CamelCase($vo_POST.vt_FunctionName)]. Une fonction déclarée Function TogglePublish est donc cherchée sous le nom togglePublish et n'est pas trouvée : la requête revient sans rien avoir fait. Déclarez vos fonctions en camelCase.

Ce que reçoit la fonction

Function togglePublish($vo_POST : Object)->$vo_WebResponse : cs.bspkComponent.WebFormController
    $vo_WebResponse:=cs.bspkComponent.WebFormController.new()
    …

Le répartiteur passe en réalité quatre arguments : $vo_POST, Entity, la session et un objet d'information. Vous pouvez donc déclarer plus d'un paramètre si vous en avez besoin.

$vo_POST n'est jamais Null dans BSPK_WEB_ON_CONNECTION : le déclarer puis tester s'il est Null en tête de méthode, c'est tester la mauvaise chose.

Faire arriver les valeurs du formulaire dans $vo_POST

Rien n'est envoyé automatiquement. Vous déclarez ce dont vous avez besoin dans un bloc de commentaire /* parameters */, juste sous la déclaration de la fonction :

Function createPayment($vo_POST : Object)->$vo_WebResponse : cs.bspkComponent.WebFormController
/* parameters
vt_BlocForm:select:getBlocsNameCollection:["wrapper","accordion","modal"]:mandatory:tomSelect:label:Form
vt_SendAllObjectsOfBlocName:hidden:vt_BlocForm
*/

Deux besoins courants :

ObjectifDéclarationArrive dans
Les lignes sélectionnées d'une listboxvt_GetBlocInfo:select:getBlocsNameCollection:mandatory:tomSelect$vo_POST.{blockName}_selectedRows
Tous les champs d'un conteneurun sélecteur de bloc + vt_SendAllObjectsOfBlocName:hidden:<son nom>$vo_POST.vt_FieldValue.{blockName}

L'indirection que tout le monde rate

vt_SendAllObjectsOfBlocName contient le nom de l'autre paramètre, sous forme de texte. Jamais un uuid, jamais un nom de bloc :

"vo_Param": {
  "vt_BlocForm": "1DAC5696B4EB4A6A8C3ACB34E25A37C5",   // the block's uuidKey
  "vt_SendAllObjectsOfBlocName": "vt_BlocForm"          // the PARAMETER NAME
}

En cas d'erreur, vt_FieldValue arrive vide ({}) alors que la fonction s'exécute normalement et annonce un succès. Le diagnostic est toujours le même : regardez le corps de la requête /bweb/call-action dans l'onglet réseau du navigateur.

Deux pièges voisins, tous deux silencieux :

  • Un champ sans vt_FieldName est retiré par le contrôle de sécurité : il n'atteint jamais vt_FieldValue.
  • Les valeurs arrivent en texte, y compris pour le onChange d'un champ isolé. Convertissez-les explicitement, et utilisez BSPK_STRING_TO_REAL plutôt que Num() pour les décimaux.

Répondre : le WebFormController

L'objet renvoyé est ce qui agit sur la page : sendAlert, reloadBlock (un bloc par appel, pas de collection), redirectToRoute, l'ouverture ou la fermeture d'un modal. Il est sérialisé dans la réponse et rejoué côté client.

Un sendAlert suivi d'une redirection détruit le toast : la page se recharge avant qu'on ait pu le lire. Quand le message porte une information, ne redirigez pas, ou utilisez sendAlertAfterRedirect.

Quand rien ne se passe

Par ordre de probabilité :

  1. Le contrôle de sécurité a refusé le POST. Cherchez l'erreur JSON sur /bweb/call-action, pas un bug dans votre fonction : elle n'a jamais été appelée.
  2. La fonction n'est pas chargée (fonction nouvelle, pas de BSPK_REFRESH_STORAGE). L'appel d'une fonction non chargée reste bloqué au lieu d'échouer.
  3. Le nom n'est pas en camelCase.
  4. La classe n'a pas d'extends : le panneau ne l'a jamais proposée et l'événement est à moitié configuré.

Voir aussi

  • La sécurité : ce que vérifie le contrôle, et pourquoi il retire des champs
  • Bouton : les propriétés du bloc qui porte le plus souvent l'événement