Changement récent dans le comportement des inputs de filtre en liste?

Tags: #<Tag:0x00007f2f2ea7e6a8>

Bonjour,
nous avons observés un changement récent dans le comportement des inputs de filtre en liste…

Auparavant, le fait de renseigner une valeur de filtre dans un input déclenchait l’application du filtre (probablement bindé sur le onchange). Désormais, il faut explicitement appliquer le filtre en appuyant sur entrée (ou autre action comme “Rechercher” si un champ est présenté avec des cases à cocher).


Est-ce voulu ou est-ce une régression?

Ce changement est voulu. Le but est d’améliorer l’expérience utilisateur tout en évitant des search intermédiaires inutiles.

Nous sommes perneurs de toute suggestion allant dans ce sens.

Bonjour David,
merci beaucoup pour ton retour rapide et la confirmation.
à l’origine de cette demande, nous avons une code spécifique qui synchronise les filtres en liste avec un TCD (un clic sur une cellule de la table déclenche la mise à jour des filtres de la liste) et ce code ne fonctionne plus (il s’appuyait sur l’hypothèse d’un onchange de l’input). Nous allons revoir ça.

C’était surtout une anomalie.
Si l’utilisateur faisait ENTER = lance la recherche qui provoquait un blur sur l’input donc un onchange = 2 search. L’utilisateur le voyait pas, mais on avait bien 2 appels Ajax.

et en terme d’UX c’est à l’utilisateur de saisir ses filtres et de lancer la recherche = on peut voir pour ajouter un bouton “do search” si le ENTER n’est pas assez intuitif, mais ça va alourdir la UI.

Vous pouvez toujours binder des “change” sur les input via le hook front list.onload.

Merci François pour ton retour.
Nous allons en effet adapter notre code pour déclencher le search.
Cependant, suite à cette évolution, si je sélectionne une option d’énum, aucun search n’est appliqué et l’action “enter” provoque uniquement l’ouverture ou le fermeture du popup d’options:
image.

Que doit faire l’utilisateur pour déclencher la recherche si aucun filtre n’est défini sur un champ texte (avec “enter” pour valider) ?

Si le champ énuméré est rendu avec des cases à cocher, il y a un bouton “Rechercher” mais dans le cas d’un popup de sélection simple, il n’y a rien.

En fait, il y a deux problèmes:

  1. un problème d’UX: comment déclencher la recherche suite à la sélection d’une option de popup ou à la modification d’un input texte libre (pour l’input texte, c’est “enter”)
  2. un problème technique restreint à notre cas spécifique: comment depuis un code front renseigner des filtres de liste puis déclencher la recherche (actuellement, c’est un code jquery “quick & (very very) dirty” dont l’implémentation ne peut décemment pas être exposée sur ce forum)

Dans les deux cas, je m’en remets à votre expertise :)

Pour le 1)

  • Effectivement si c’est un select “simple” ça ne marcha pas tout seul puisqu’il n’y a pas le bouton “Rechercher” : on va devoir remettre un onchange ou un bouton dans ce cas puisqu’il n’y a pas de ENTER non plus.
  • La UI legacy avait un bouton loupe sur chaque champ mais ça prend de la place et c’est moche.
  • On avait aussi un problème de gestion du TAB si on laissait le “onchange” lancer la recherche. au rechargement on ne passait pas au champ suivant…
  • le onchange automatique était une vraie fausse bonne idée

Pour le 2) il suffit de mettre les filtres UI dans l’objet + relancer l’affichage de la liste :

en gros ce que faisant le onchange :

$ui.view.tools.readSearch(ctn, obj);
$ui.displayList(ctn, obj, { showNav:true, nav:"add" });

(si ctn = null ça veut dire la fenetre principale)

Pour valoriser les filtres, je ne comprends pas le besoin de passer par les input/select UI, ça reste du pur controller :

obj.filters = { myfield: "blahblah", order__myfield: -1 };
$ui.displayList(ctn, obj, { showNav:true, nav:"add" });

Bonjour François,
merci beaucoup pour ton aide.
ça fonctionne parfaitement.

Pour lever le voile sur la partie qu’on ne saurait voir (nous avions échangé à ce sujet lors du dernier club utilisateur avec David), nous synchronisons un tableau croisé avec une vue liste du même objet en positionnant par code front les valeurs de filtres des champs définis en ligne/colonne du tableau:

Le code que tu nous as fourni nous a permis de simplifier énormément la partie concernant le positionnement des filtres et le refresh de la liste.

Nous avons par ailleurs d’autres instructions consistant à farfouiller dans les metadata pour faire le match entre les entêtes de ligne/colonne de la table HTML et l’ObjectField correspondant à chaque axe défini en ligne/colonne du TCD ainsi que le code de la valeur dans le cas des champs énumérés (voire la combinaison des valeurs dans le cas des énumérations multiples; quoi que nous n’ayons jamais rencontré le cas à ce jour).

Pour finir, nous avons une instruction qui bind un callback sur clic des cellules de la table HTML.

Cependant (et je me remémore ce dont nous avions discuté), si la cellule de la table du TCD pouvait comporter dans ses data-attributes la désignation de l’ObjectField , notre solution serait beaucoup moins indécente…

<td class="ctcell numeric BCSIHome_pointer" data-row-1-objectfield="FamCoSurveyCalcMix1" data-col-1-objectfields="FamCoSurveyStatus" data-row-1-code="TBC" data-col-1-code="ALL">6</td>

NB: il se peut que certains de nos TCD soient structurés sur plusieurs lignes ou colonnes (d’où le -n- dans les data-attributes)

Ca me rappelle effectivement une discussion… notée sur un post-it de David et pas dans le backlog officiel ;-)

Il faut effectivement qu’on améliore le TCD notamment pour lui permettre d’être éditable dès lors qu’une cellule est identifiée par la ligne/colonne et que ce n’est pas un agrégat.

Avec jQuery on peut associer des objets, donc je vois plutôt associer qq chose comme :

$('.ctcell').data("meta", {
  row: [ { field:"name1",value:"val1" }, { field:"name2",value:"val2" } ],
  col: [ { field:"name3",value:"val3" } ]
});
  • à savoir la liste des champs ordonnés en ligne/colonne avec leurs valeurs.
  • facilement accessible ensuite par code via $(this).data("meta") si on met un click sur chaque .ctcell

J’ajoute le besoin au backlog, ça devrait pas être compliqué à ajouter.

1 Like

J’ai créé un autre post pour suive ce nouveau besoin :

Bonjour François,

oui, merci beaucoup pour ta réactivité.
Amandine est déjà sur le coup :)

ps: Environnement de DEV redéployé / Image docker BuiltOn=2020-06-29 17:45 (revision 5c600ab706a7da7a6d6abc91aa357a81118df4e5)

Bonjour,
j’ai mis en place votre solution. Merci pour son implémentation rapide.
J’ai toutefois un soucis :
lors du 1er appel, les filtres se placent bien. En revanche lors du deuxième appel, le champs déjà filtrés restent en place. Ex : j’ai d’abord cherché maValeur quand je clique sur maValeur2, l’appel reste sur maValeur.
Après investigation, l’appel au 1er search donne ceci :
image
Le deuxième appel donne :
image
Il y a des champs rajouté automatiquement par la plateforme qui utilise __ au lieu de . et qui “annule” le vrai filtre.
–> j’ai contourné en remplaçant . par __ mais je trouve dommage de le faire manuellement.

	$(".ctcell").css("cursor","pointer").click(function(e) {
		e.stopPropagation();
		var meta = $(this).data("meta");
		  //console.log("click = " + JSON.stringify(meta));
		var filters = {};
		if (meta.col && meta.col.length) {
			meta.col.forEach(function (col) { 
				filters[col.field.name.replace(".", "__")] = col.value;
				
			});
		}

Petites questions également, lorsque le tableau est rechargé,

  • la zone de recherche n’étai plus en colonne mais devient une zone à droite de l’écran. Comment puis-je rétablir la préférence ?
  • une liste déroulante apparaît contenant les recherches prédéfinies. Existe t-il une option pour les masquer ?

Petite amélioration : lorsqu’il y a une ligne de total, la valeur se met dans les filtres. Il faudrait que cette valeur soit ignorée.
image

Pour info également la ligne console.log("click = " + JSON.stringify(meta)); génère l’erreur suivante :
image

Cordialement

Merci pour les retours, qu’il aurait fallu faire sur l’autre post du coup…

  • sur le noms des attributs ramenés effectivement, on va ajouter un champ name à côté de field et value avec la syntaxe front “__” au lieu du “.” (mais ne pas toucher à l’objet field utilisé par ailleurs)

  • la liste peut s’ouvrir avec une option de recherche en colonne, cf paramètre “search” d’une liste :
    https://docs.simplicite.io/4.0/jsdoc/Simplicite.UI.Globals.list.html
    $ui.displayList(..., { search: false|'column'|'popup'|'docked' });

  • le stringify doit planter à cause d’une référence croisée entre grant et app, dommage c’est souvent pratique pour debugger. je vais le retirer de l’exemple donné.

  • le recherches prédéfinies sont maintenant accessibles directement depuis la liste sans avoir à faire 5 clicks (vous n’êtes pas les seuls à avoir des besoins urgents-bloquants faits au mieux en attendant la V5 ;-). Il n’y a pas de paramétrage pour les masquer pour le moment par objet, il faudra nécessairement faire du CSS (global ou local) :

    .objlist .predef-search { display: none; }
    .objlist.object-MyObject .predef-search { display: none; }

    Il est prévu d’ajouter :

    • un accesseur pour pouvoir les masquer par code dans le postLoad/initList
    • un choix utilisateur dans les préférences de liste pour les avoir ou pas en accès direct

Bonjour,
désolée, je n’ai pas osé commenter sur l’autre post. Vu qu’il s’agissait d’un “mode d’emploi”, je trouvais qu’il était mieux de le laisser le plus court possible. :S
Merci pour vos retours.
Pas de soucis pour les recherches prédéfinies, je préférais poser la question au cas où vous aviez prévu le coup plutôt que de partir sur une solution “home made”.

Encore une fois merci pour vos rapides retours.

Bon finalement je n’ai pas les droits pour poster sur l’autre ticket.

En appliquant cette méthode sur tous mes autres tableaux, j’ai constaté un bug sur les champs de type enum sur les rows : le libellé est pris comme valeur à la place du code.
image

Merci d’avance

Encore un autre point :
sur ma vue, j’ai plusieurs tableaux croisés avec évidemment des critères différents :

Lorsque je clique sur le 1er tableau, des filtres se positionnent. Lorsque je clique sur le 2e tableau, des filtres se positionnent également. Les filtres communs sont bien écrasés mais les filtres du tableau 1 reste.
Exemple :
tableau 1 Critère { Col : Status, Row : MixImpact }
tableau 2 Critère { Col : Status, Row : Domain}
Clic sur une cellule du tableau 1 --> mise en place des filtres : Status = Done, MixImpact = Impact code famille

Clic sur une cellule du tableau 2 --> mise en place des filtres : Status = Started, Domain = DIR-A

–> lors de ma deuxième recherche, le filtre MixImpact est toujours positionné.

Existe-t’il une façon de dire que le nouveau filtre écrase totalement le 1er ?

Cordialement

  • L’accesseur setPredefSearchOnList a été ajouté pour permettre de masquer le dropdown sur la liste.
  • Sur les filtres, quand ils sont passés en paramètre du displayList, ils sont fusionnés avec les anciens (sur-filtrage). il faut juste écraser tout l’objet filtre avant obj.filters = {}, coté serveur il y aura un resetFilters() avant d’appliquer ceux envoyés. Sinon vous pouvez aussi ajouter des reset suivant votre navigation { MixImpact: '%' ... } lors de l’appel pour être sûr.
  • On va regarder pour mettre les codes sur les lignes comme pour les colonnes

Bonjour,
merci pour votre retour. Cela dit vos solutions

il faut juste écraser tout l’objet filtre avant obj.filters = {} …Sinon vous pouvez aussi ajouter des reset suivant votre navigation

implique d’avoir accès à l’objet. Or dans notre exploitation des méta data du cross tab nous avons pas d’entité object. (Nous avons rusé un peu en le récupérant depuis les méta de la cellule mais j’ai un doute sur la pérennité de cette méthode). Je trouve dommage de ne pas avoir le choix entre une fusion des filtres ou un remplacement.

function addListenerOnCell(crossTable, crossTabDiv, searchContainer ) {
	crossTable.find(".ctcell").css("cursor","pointer").click(function(e) {
		e.stopPropagation();
		var meta = $(this).data("meta");

		var metaFilter = meta.col ? meta.col : [];
		if (meta.row && meta.row.length) {
			metaFilter = metaFilter.concat(meta.row);
		}
		if (metaFilter.length) {
			var filters = {};
			// Reinit all field
			metaFilter[0].field.field.object.metadata.fields.forEach(function (objectField) { 
				if (objectField.searchable > 0 && !filters[objectField.name]) {
					filters[objectField.name] =  "%";
				}
			});
			// Place new filter
			metaFilter.forEach(function (filter) {
				if (filter.value != "Total") {
					var value = filter.value;
					if (value === " ") {
						value = "is null";
					}
					if (value === "" && filter.field.field.listOfValues) {
						value = "%";
					}
					filters[filter.name] = value;
				}
			});
			console.log([
				"Filter search from crosstable",
				"Obj = " + metaFilter[0].field.field.object.metadata.name,
				"search container = " + searchContainer,
				filters
			]);

			$ui.displayList(searchContainer,  metaFilter[0].field.field.object.metadata.name, {
		      showNav : false,
		      nav : "add",
		      filters : filters,
		      search : 'column'
		    })
		}
	});
}

Petit soucis (mineur cela dit), dans le cas d’un tableau croisé dont un des axe est défini sur l’attribut suivant :
image
alors que l’affichage rend ceci :
image
La valeur de la métadata est
image

Savez-vosu pourquoi tous ces zéros sont ajoutés ?

Cordialement
Amandine TRIDOU

Le filtre de la méthode UI displayList est un surfiltre normalement saisie par l’utilisateur sur les filtres du de l’objet Ajax/session. Il existe également les fixedFilters si vous souhaitez que l’utilisateur ne puisse pas les modifier mais juste les voir.

Dans votre cas si vous naviguer sur l’instance principale de l’objet, il suffit donc de récupérer l’objet de session et lui changer tous ses filtres avant affichage :

var obj = $ui.getAjax().getMainObject("objectname");
obj.filters = { field: "abc" ... };
$ui.displayList(div, obj, ...);

La méthode displayList (comme beaucoup d’autres) peut prendre en paramètre l’instance d’objet souhaité et pas uniquement son nom.

Le padding avec des “0” sert généralement à trier des valeurs textuelles.
Vous pouvez toujours les reformatter via des parseInt / parseFloat mais Simplicité utilise du texte pour véhiculer des chiffres ou des enum dans les TCD = ne pas perdre en précision sur les big-decimaux notamment, ou trier des codes de liste faussement numériques…

Bref ne pas confondre le M du V dans le modèle MVC.