Side Effects, quel est ce sortilège ?

Traitement en cours…
Terminé ! Vous figurez dans la liste.

Plongeons dans le cœur du modèle RAP d’ABAP, où ces mystérieux side effects jouent un rôle essentiel. Conçus pour optimiser l’expérience utilisateur dans les applications Fiori en mode Draft, ils permettent de synchroniser intelligemment l’interface graphique sans surcharger le backend.

1. Mais au fait, c’est quoi un « side effect » en RAP ?

Les side effects ont pour vocation de permettre a l’UI d’être rafraîchit dans le cas d’un RAP BO construit en mode DRAFT. En effet :

  • Dans le cas d’objet RAP en mode Draft, le « stateless communication pattern » est utilisé. C’est à dire que l’UI ne vient pas recharger tous les champs de l’objet une fois un des champs modifié.
  • Le gros avantage est un fonctionnement plus efficace des requêtes, car sinon il y aurait trop de requêtes, lenteurs, surcharges réseau, s’il fallait tout recharger à chaque fois.
  • Par exemple, si un des champs de l’objet RAP est modifié par le user, une requête PATCH va être effectuée vers le BO RAP, et si la requête est un succès, il y aura un retour 204 No content ( Classique pour une requête PATCH ). Cependant, imaginons que la modification de ce champs déclenche également une détermination sur un autre champs. comme il n’y a pas de READ effectue juste après, l’UI n’est pas mise a jour avec la nouvelle valeur.
  • La solution est donc le Side effect. Le side effect va permettre, dans notre cas d’exemple de préciser que lorsque ce champs en question est modifié, alors il faut aussi recharger l’autre champs.
  • Lorsque side effet est créé, les metadatas sont modifiées pour indiquer a l’UI qu’un side effect existe est qu’il faudra recalculer certains champs lors de certaines modifications.

Pour essayer de résumer la documentation, il est possible de créer des side effect pour différents cas :

  • Field : Quand un champ spécifique est modifié, cela déclenche le side effect ( exemple expliqué ci-dessus )
    side effects { field MyField affects Targets }
  • Action : Quand une action utilisateur est exécutée (ex: Calculate, CheckAvailability), cela déclenche le side effect et donc l’UI sera rechargée dans ce cas la.
    side effects { action MyAction affects Targets }
  • determine action : Quand une détermination RAP est déclenchée, cela peut également provoquer un side effect.
    side effects { determine action MyDetermineAction executed on Sources affects Targets }
  • self : Quand l’entité elle-même (n’importe quel champ) est modifiée, cela peut déclencher des rechargements d’autres entités, mais pas elle-même.
    side effects { $self affects Targets }
  • event : Quand un événement est levé côté backend, cela peut déclencher un side effect. Nous y reviendrons plus tard via un exemple concret.
    side effects { event MyEvent affects Targets }

C’est différents cas vont permettre de recharger différentes Targets , en effet, dans un side effects block, un target désigne ce que l’on souhaite recharger côté UI lorsque le side effect est déclenché.

  • field : Les champs qu’on souhaite recharger
  • permissions : Permet de recalculer les autorisations
  • $self : Recharge l’intégralité de l’entité elle-même (pas juste un champ).
  • entity : Recharge une entité associée complète via une association CDS
  • messages : Recharge les messages d’erreur / warning / info

Maintenant que les objectifs des side effects sont clairs, passons a nos cas d’exemples.

2. Cas d’étude

Tout le code est disponible sur notre Gitlab.

Pour notre cas, nous allons créer un objet RAP en se basant sur cette table :

Cette table a pour objectif de stocker 3 champs textes ainsi qu’un statut dont nous baserons la valeur en fonction des différents champs texte => via une détermination dans l’objet RAP.

Puis nous utilisons le ABAP RAP Generator Objects pour générer un objet RAP Draft-Enabled.

Les règles que nous allons mettre en place :

  • Lorsque le champs text est rempli, il doit être impossible de remplir le champs text2
  • Lorsque le champs text est rempli, sa valeur doit être répliquée sur le champs text3
  • Lorsque le champs text2 est rempli, il doit être impossible de remplir le champs text
  • Enfin, a la sauvegarde de l’objet, nous souhaitons qu’un traitement via bgPF soit effectué pour déterminer le statut. Une fois ce traitement asynchrone effectué, il faut donc que l’UI soit mise.

Voici via cette vidéo, le résultat attendu.

PARTIE 1 : Gestion des champs text, text2 et text3

  • Pour gérer le fait que les champs text et text2 ne soit pas remplis en même temps, il suffit d’implémenter la méthode get_instance_features et de désactiver le champs qui ne doit pas être remplis :

Ici, si le champs text est rempli, alors le champs text2 passe en read only via la constante if_abap_behv=>fc-f-read_only attribuée a %field-text2 du parametre RESULT.

Idem dans l’autre cas.

  • Ensuite, pour que le text3 soit identique au champs text, nous créons une détermination qui sera exécutée à la modification du champs text
  • Au final, le behavior definition ressemble à ceci pour le moment

Cependant, nous voyons qu’il y a un problème :

En effet, dans la vidéo, il est possible de voir que les champs ne sont pas mis a jours.

Quand nous observons les appels http, nous voyons que lors de la modification des champs texte, un PATCH est envoyé avec succès

avec comme réponse 204 – No Content ( Normal en cas de succès avec un PATCH )

Le problème ici est donc que l’application Fiori n’a aucun moyen de savoir que les caractéristiques des champs text2 et text3 ont également été modifiées.

  • Maintenant, en ajoutant les side effects au behavior definition : cela fonctionne ( Cf. première vidéo )

Voici désormais le behavior definition :

Ici en ajoutant field text2 affects $self; et field text affects $self; j’indique a l’UI qu’il faut recharger l’entité complète lorsque les champs text ou text2 sont modifiés. Cela permet de recharger les feature controls ( et donc griser les champs souhaités ) ainsi que d’updater le champs text3, puisque tous les champs sont rechargés.

Le PATCH est toujours envoyé, mais cette fois-ci, les feature controls et les champs sont rechargés via un GET effectué juste après car, via les metadatas, l’application Fiori sait que lors de cette modification, il faut re-charger l’ensemble de l’entité.

PARTIE 2 : Update de l’UI lors du calcul du champs status en arrière plan via bgPF

Notre objectif ici est de démontrer qu’il est possible d’updater l’UI, même une fois la sauvegarde de l’objet effectuée et terminée, dans le cas où des modifications en arrière plan de manière asynchrones seraient effectuée. Pour cela, nous allons utiliser les Event-Driven Side Effects.

Ces event-driven Side Effect permettent de mettre a jour de l’UI lors de processus asynchrones.

Concrètement, ici, lors de la sauvegarde de l’objet, nous allons ajouter une modification asynchrone via bgPF. Ce dernier viendra modifier la valeur du champs status en fonction des valeurs de textes ( Si au moins un champs texte est rempli, on passe le statut en non « Validated », sinon en « No Validation ». Pendant l’ensemble du temps de traitement, le champs sera laissé en « Waiting for validation » pour montrer au user qu’un calcul est en cours ). Dans le cas de cet exemple, et pour montrer que cela est utile dans le cas des traitements long, nous allons ajouter un WAIT de 4 secondes ( Comme c’est en bgPF, la modification de manière synchrone est terminée et le user peut reprendre la main même si le traitement asynchrone est encore en cours ).

  • Nous créons tout d’abord une action qui sera appelée par le bgPF via EML : action calculateStatus result [1] $self;
  • Puis nous créons la classe pour le bgPF :
  • Enfin, dans le behavior definition, nous ajoutons with additional save et, dans la méthode save_modified, nous venons créer le bgPF en utilisant la classe zbgpf_UI qui viendra modifier le champs status.
  • Nous effectuons cette action : RAISE ENTITY EVENT zr_crt_test~statusUpdated afin de venir informer via un event que le champs status a été modifié et l’UI sait qu’il faut recharger

Comme le side effect est bien present dans le behavior definition : event statusUpdated affects field ( Status, Criticality ); l’UI Fiori vient créer un listener sur l’event statusUpdated, et quand il est émis suite à la modification effectuée via bgPF, l’UI effectue un nouveau GET vers le RAP BO pour récupérer les informations mises à jour ( ici, Status et Criticality ).

Conclusion

Vous savez desormais mettre à jour l’UI de vos Fiori app en utilisant le Draft. Egalement, vous avez pu voir que les traitements en arriere plan ne vous empeche pas de mettre à jour votre UI une fois ceux-ci terminer.

Enfin, il est important de noter que ce comportement de event-driven side effect, permet egalement de mettre a jour l’UI de tous les users qui seraient connectés sur le meme objet. En effet, comme un listener est crée par l’application Fiori sur cet event, tout UI en train d’afficher l’objet modifié sera notifiée et sera mise a jour !

Laisser un commentaire