Problème de Pré-filtrage dans l'association

Bonjour,

J’ai une relation ‘1,n’ entre les objets A et B, et j’ai écrit le code suivante dans l’objet B :

protected boolean filterListStatutFlag = false;

@Override
	public void preSearch() {
			
		if (!filterListStatutFlag || isPanelInstance()){
			filterListStatutFlag = true;
			setFieldFilter(Field,Filter);
		}
}

Lorsque je supprimer le filtre depuis la liste “Menu”, le filtre est supprimé, mais je ne peux pas le supprimer dans l’onglet/association “Panel”.

J’espère avoir été clair dans mes explications

Avant de répondre sur le cas particulier, je rappelle que de manière générale il ne faut jamais utiliser de variables de classe dans les objets métier Simplicité

Je cite le § à ce sujet dans le pre audit que je vous ai rédigé il y a quelques semaines:

Usage de variables de classes

L’usage de variables de classe (à priori pour conserver les valeurs courantes d’attributs entre appels de hooks) est un anti-pattern:

Ex: ans la classe NamJeune:

protected String nom;
protected String prenom;
(...)

Il convient de systématiquement utiliser les getField/getFieldValue/setFieldValue à chaque appel. En effet, plus votre application se complexifiera, notamment si vous utilisez des héritages ou des hooks qui ne sont pas appelé au même moment, moins vous aurez la garantie qu’une valeur n’aura pas été modifiée au niveau de l’objet entre 2 appels de hooks par un mécanisme géré dans le moteur Simplicité (ex: par une contrainte).

Pour simplifier il faut considérer que chaque appel de hook est “stateless” au niveau de cette classe d’objet, seuls les accesseurs garantissent que vous accedez aux valeurs courantes des attributs de l’objet. Il faut donc proscrire tout usage de variables de classe.

Si vous devez conserver des données autres que des valeurs d’attributs entre deux appels de hooks utilisez get/setParameter (cf. cette Javadoc et ses variantes) cela stockera la valeur à un endroit similaire à celui ou est conservée la valeur courante des attributs de l’objet.

NB: Ces considérations seront particulièrement importantes dans les versions futures de Simplicité où on utilisera un “cache” distribué dans le cas des clusters sans load balacing sticky session. Dans ce contexte chaque appel de hook pourra potentiellement être effectué sur un noeud Simplicité différent qui n’aura donc potentiellement pas en mémoire le valeurs des variables locales d’un autre noeud.

Bonjour @david,

Vous avez oublié de répondre à ma question

Je ne répond pas à la question car le code que vous avez indiqué ne va pas => la variable de classe
filterListStatutFlag n’est pas souhaitable pour les raisons indiquées

Réécrivez votre code sans utilisation d’une variable de classe et on verra si vous avez toujours votre question.

Oui votre code positionne un filtre en dur sur l’instance Panel via isPanelInstance(). C’est donc un problème d’algorithme qui ne répond pas à votre besoin.

Entre les lignes de votre code, je suppose que vous souhaitez mettre un filtre une première fois sur un objet en Panel puis permettre à l’utilisateur de le retirer ?

Pour adresser ce besoin, on ne peut pas forcer un filtre à chaque search (sauf à gérer un lock dont il faut gérer la remise à 0 quelque part = quand on change d’objet A).

Par exemple il faut laisser l’objet B “libre” sans code, et forcer un filtre depuis l’objet A qui contient ce panel. Dans l’initUpdate de l’objet A (appelé 1 fois quand on ouvre un formulaire A), il faut ajouter un filtre dans l’instance Panel B :

public void initUpdate() {
   if (isMainInstance())
     getGrant()
       .getPanelObject("objectB","foregnKeyField")
       .setFieldFilter("myfield", "abc");`
   super.initUpdate();
}

Mais je n’ai peut être pas bien compris votre besoin.

De ce que je comprends, si on utilise la méthode setFieldFilter() au sein de l’objet concerné (dans le preSearch par exemple), le filtre est définitif et ne peut être retiré par l’utilisateur.

Par contre si on l’appelle depuis l’objet parent, l’utilisateur peut supprimer le filtre par défaut, c’est bien ça ?

Le search est appelé dès qu’on fait un select SQL sur l’objet (donc une liste ou un formulaire)

  • Le initList/preSearch/postSearch est appelé à chaque recherche donc il se remettra à chaque accès suivant votre algorithme.
  • Le initUpdate/preSelect/postSelect a chaque formulaire

J’ai essayé le code que je vous ai donné pour forcer un filtre au niveau du fils via le code du père, le filtre est effacé lors de l’affichage du panel fils, donc ça ne marchera pas dans votre cas.

Il faut donc utiliser un flag qui indique qu’il faut ajouter un filtre au niveau du initList ou pas.
A mon sens s’est compliqué de prévoir tous les cas de gestion où il faudra désarmer/réarmer ce flag.

Déjà je le ferai dans le initList qui est appelé avant affichage de la liste 1 seule fois. Alors que le preSearch est appeler pour le count, pour la liste, pour le select d’un row_id. Ensuite il faudra comme l’indique David le positionner sur un paramètre en mémoire pour que tous les objets puisse acceder/changer ce flag :

Dans l’objet père A, quand on ouvre un formulaire, on arme un filtre …

public void initUpdate() {
   if (isMainInstance())
       setParameter("MON_FILTRE_X", "abc");
   super.initUpdate();
}

… Que l’objet fils B viendra consommer une seule fois :

public void initList(ObjectDB parent) {
   if (parent!=null && parent.getName().equals("A")) {
     String p = parent.getParameter("MON_FILTRE_X");
     if (p!=null) {
       setFieldFilter("myField", p);
       parent.removeParameter("MON_FILTRE_X"); // une seul fois
     }
   }
   super.initList(parent);
}

Sinon d’autres idées :

  • Si le besoin est d’offrir des recherches prédéfinies à l’utilisateur, c’est une fonction standard en V5 accessible directement en liste (cf release-note)

  • Vous pouvez aussi créer des actions de Liste qui positionnent des filtres automatiquement avec du code très simple en back :

    • bouton “Tout voir” = resetFilters()
    • bouton “Aujourd’hui” = setFieldFilter("mydate", Tool.getCurrentDate())
    • bouton “En cours” = setFieldFilter("mystatus", "RUNNING")

Car préfiltrer “en dur” répondra en général à un unique cas d’usage et pas tous les profils qui ont besoin de voir des choses différentes par défaut. Personnellement, je faisais plutôt ce genre de boutons raccourcis en V4 et désormais des recherches prédéfinies en V5.

Merci pour cette réponse, je comprends mieux la problématique. J’avais pas compris que la suppression du filtre relançait forcement un search derrière …

Donc, j’ai mis en œuvre votre solution mais à l’affichage du parent, la méthode s’exécute plusieurs fois pour une raison qui m’échappe, donc le filtre est appliqué puis a la deuxieme execution il ne l’est plus. Je regarde si y’a pas une explication dans le reste du code parent.

Oui le preSearch au niveau du fils B est appelé 2 fois = 1 pour compter les lignes (select count) + 1 pour chercher la page à afficher (select rows)

Si vous voulez faire le travail une fois, il faut le faire dans le initList de B qui lui est appelé 1 seule fois par affichage de la liste (dans mon exemple à chaque fois qu’on ouvre un formulaire A, le panel B appelera le initList avant le search = count + select rows).

Mais j’en reviens à ma question initiale, quel est votre besoin en terme d’usage ?

Le besoin est de proposer un filtre par défaut sur l’objet B à l’utilisateur tout en lui laissant le choix de retirer ce filtre.

Ce que je constate, même en utilisant le initList, c’est que quand j’ouvre l’objet A, l’initList est appelé mais le panel contenant la liste des objets B est fermé par défaut et lorsque l’on clique sur l’onglet de B, l’initList est appelé de nouveau.

D’ailleurs, je comprends pas que toutes les jointures partant de A soient faites au chargement de ce dernier alors que dans chaque panel on refait systématiquement le calcul lorsqu’on y accede. Dans notre cas on a 8 panels. Peut on configurer une désactiver le chargement “eager” pour le faire uniquement à l’accès au panel concerné ?

Bonjour,

Merci pour l’explication, donc comme indiqué gérer un flag “filtre ou pas filtre” sera compliqué suivant l’ensemble des usages de l’objet.

La question à poser à l’utilisateur c’est quand il faudra lui remettre le filtre (réarmer un flag une fois passé le premier usage/retrait du filtre). On tombe toujours dans des cas impossibles à gérer suivant la navigation et le contexte d’usage (accès direct, en panel, quand on change de A, quand on change de page sur la liste B, quand on fait des actions en masse sur B, export de liste, filtrage classique en plus, recherches prédéfinies avec préférence, etc.).

La solution de lui mettre 2 boutons simples (tout | filtre) répond à tous les cas en 1 click de sa part.

Quand vous parlez de jointures je pense que vous parlez de “search” :

  • Il est nécessaire de faire tous les “select count” de chaque onglet pour afficher les badges (les select count passent par un preSearch pour appliquer les éventuelles règles et font nécessairement les mêmes jointures qu’une recherche de records),
  • Les listes paginées sont bien ramenées qu’à la sélection de l’onglet (de nouveau appel au preSearch qui est un hook de bas niveau, et est également appelé quand on sélectionne un ligne pour les même raisons de filtrage spécifique).

Le initList n’est appelé que sur le search de page, pas sur un count donc à mon avis mieux adapté à gérer ce flag.