Impossible de sortir du field.change(...) dans le Javascript

Bonjour,

Je constate un comportement étrange lors de l’exécution de la fionction uiField.change(fonction(){…});, et uiField.on(“change”,fonction(){…});

Le but est de remplir des cases nom-adresse-codepostal-ville-siret à l’écran à partir d’une liste de suggestion d’organismes présente dans le field Nom

Le format est le suivant :

  • Nom - Siret - Ville - Code postal - Adresse
    image

Le comportement actuel est le suivant :

Lorsque je tape mon organisme à rechercher, cela fonctionne et me propose la liste, lorsque je sélectionne la liste, l’autocompletion fonctionne.
image
Cependant, lorsque je clique sur n’importe ou sur l’écran, ma fonction d’autocompletion est rapellée, et est appliquée sur ce qu’il reste dans le champ Nom.

Je reste bloqué dans la fonction uiField.change(…) et n’en sort jamais. ( voir console à la fin )
Une parade à cela est d’appuyer sur espace une fois une ligne de la liste de suggestion sélectionnée pour sortir du champ.

Je ne vois pas dans mon code ci-dessous ce qui pourrait causer ce blocage, Est-ce que cela viendrait des différent Hooks qui viendraient perturber ce comportement normalement simple.

Bien cordialement

// LadFormations front side hook

(function(ui) {
    if (!ui) return;
    var app = ui.getAjax();
    // Hook called by each object instance
    Simplicite.UI.hooks.LadFormations = function(o, cbk) {

        		function bindnomorg(ctn,obj) {
        			
            		var idnom = $("#field_ladFormationsNomdelorganisme_idladFormations_LadDossierPmfp_id");
                    var idsiret = $("#field_ladFormationsSiretOrganisme_idladFormations_LadDossierPmfp_id");
                    var idcp = $("#field_ladFormationsCodePostalOrganisme_idladFormations_LadDossierPmfp_id");
                    var idville = $("#field_ladFormationsVilleOrganisme_idladFormations_LadDossierPmfp_id");
                    var idadresse = $("#field_ladFormationsAdressedelorganisme_idladFormations_LadDossierPmfp_id");

                   idnom && idnom.change(function(){
                        console.log("Début du CHANGE");
                        // récupération de la valeur du nom
                        var valNom = idnom.val();
						
                            // récupération du code postal et du siret
                            var chiffres = valNom.replace(/[^0-9\s]/g, '');
                            console.log("Récupération des chiffres", chiffres);
                            chiffres = chiffres.split(' ');
							// rechercher le code postal (5 caractères) et le siret (14 caractères)
                            for (i = 0; i < chiffres.length; i++) {
                                var tmp = chiffres[i];
                                console.log(" longueur : ", chiffres[i].length)
                                if (tmp.length == 5) {
                                    var chainecp = tmp;
                                    console.log("code postal : ", chainecp);
                                } else if (tmp.length == 14) {
                                    var chainesiret = tmp;
                                    console.log("siret : ", chainesiret);
                                }
                            }
                            // récupérer l'index du cp et siret pour séparer la chaine de caractère 
							
                            var indexofCP = valNom.indexOf(chainecp);
                            var indexofSiret = valNom.indexOf(chainesiret);

							// l'adresse se situe après le cp, donc 5 caractères + un espace = le début de l'adresse
                            var chaineadresse = valNom.substring(indexofCP + 6, valNom.length);
							// le nom de la ville se situe après le siret, donc 14 caractères + un espace = le début de la ville
                            var chaineville = valNom.substring(indexofSiret + 15, indexofCP - 1);
							// le nom se situe au début, et s'arrête avant le siret
                            var chainenom = valNom.substring(0, indexofSiret - 1);

							// on parse les valeurs de nos chaines de caractères dans les fields
                            idnom.val(chainenom);
                            idcp.val(chainecp);
                            idsiret.val(chainesiret);
                            idadresse.val(chaineadresse);
                            idville.val(chaineville);
							
			    console.log("Fint du CHANGE");
            			});
        		}


        try {
            console.log("LadFormations hooks loading...");
            var p = o.locals.ui;
            if (p && o.isMainInstance()) {
                p.form.onload = function(ctn, obj) {

                	bindvilleorg(ctn,obj);

                 };
            };

        } catch (e) {
            app.error("Error in Simplicite.UI.hooks.LadFormations: " + e.message);
            console.log("Erreur : ", e.message);
        } finally {
            console.log("LadFormations hooks loaded.");
            cbk && cbk(); // final callback
        }
    };
})(window.$ui);

image

[Platform]
Status=OK
Version=5.1.45

[JavaVM]
Version=17.0.3
Vendor=Eclipse Adoptium
VMName=OpenJDK 64-Bit Server VM
VMVersion=17.0.3+7
ScriptEngine=rhino
ScriptEngineVersion=Rhino 1.7.13 2020 09 02
HeapFree=242343
HeapSize=397312
HeapMaxSize=509952
TotalFreeSize=354983

Bonjour,

Comment est implémentée la completion qui va cherche les résultats concaténés ?
si c’est une datamap Simplicité, il y a effectivement un change qui est déjà bindé, et Simplicité fait déjà la mapping automatiquement des champ du datamap, pas besoin de resplitter à la main.

sinon il y a forcement quelque chose qui boucle, mettez un point d’arret dans votre code (debugger) et au debugger regarder la stack d’appel UI pour retrouver quel traitement se mord la queue.

En jQuery faire un idnom.val(chainenom); ne provoque pas de nouveau change sur le champ.
donc ce n’est pas à ce niveau.

Avez vous des contraintes Front qui positionneraient d’autres changes ?

autres remarques :

comme indiqué dans un post précédent, il faut utiliser getUIField en spécifiant l’index

let idnom = ui.getUIField(ctn,obj,"ladFormationsNomdelorganisme", "ladFormations_LadDossierPmfp_id");

on pourra toujours maintenir une compat ascendante sur les API / accesseur, moins sur les id générés dans cette version.

jQuery ne retourne jamais nul, mais un tableau vide, le test à faire serait plus
idnom.length && idnom.change(...)

mais bon si idnom devient un field UI Simplicité, l’element jQuery est donnée par idnom.ui.input

autre truc peut être à faire, stopper la propagation de l’event change.

idnom.change(function(e) {
   e.stopPropagation();
   // ...
});

Bonjour,

L’erreur venait de mon traitement dans ma boucle for, en modifiant celle-ci pour la simplifier, la fonction d’autocomplétion fonctionnait bien, et n’était pas rappelée par la suite. Au lieu de récupérer tous les chiffres et de les comparer à des valeurs fixes, nous avons ajouté un caractère spécial dans notre liste de suggestion pour pouvoir séparer les différents champs à renseigner dans les fields de l’objet métier.

 let tab = valNom.split('¤');
 //console.log("__bindnomorg__tab[0]:" + tab[0] + " tab[1]:" + tab[1] + " tab[2]:" + tab[2]);

var chainenom = tab[0];
var chainesiret = tab[1];
var chaineadresse = tab[2];
var chainecp = tab[3];
var chaineville = tab[4];

C’est uniquement en changeant cette boucle for que le problème s’est résolu, même si la cause reste floue.
Solution trouvée !

Ok à mon avis vous avez 2 problèmes.
Ce n’est pas la même chose de dire que le “change” s’appelle en boucle via trigger, et que votre fonction ne termine pas. Ceci étant je n’ai toujours pas compris comment vous avez implémenté cette complétion.

  • entrée : qui va chercher les résultats et les affiche : via un datamap ? hook, code spécifique via quel event ?
  • sortie : votre méthode “change” qui découpe la réponse dans les champs.

Comme séparateur, je vous suggère d’utiliser des pipes " | " ça reste humainement plus compréhensible dans le dropdown que des ¤.

Tout d’abord j’appelle ma fonction getListeCentreFormation() située dans l’obj Métier Ladformation qui est déclenchée lorsque des caractères sont renseignés dans le champ FIELD_FORMATIONSNOMDELORGANISME (champ Nom sur les précédentes captures)

public List<String> fieldCompletion(String input, String query, String context) {
        List<String> list = new ArrayList<>();
        Grant g = getGrant();
       // Recuperation de la liste Centre de formation
        if (FIELD_FORMATIONSNOMDELORGANISME.equals(input)) {
            ObjectDB objCentre = getGrant().getTmpObject("LadRefCentreFormation");
            list = LadPmfpUtils.getListeCentreFormation(query, objCentre);
        }

La fonction getListeCentreFormation() est renseignée dans l’onglet code partagé :

   public static List<String> getListeCentreFormation(String query, ObjectDB objCentre) {
    	
        try {

            final String getListeCentre = "getListeCentre";
            List<String> list = new ArrayList<>();
            long totalNbRows = objCentre.getCount();
            AppLog.debug(LadPmfpUtils.class, getListeCentre, "totalNbRows " + totalNbRows, null);

            for (String[] row : objCentre.search()) {
                objCentre.setValues(row, false /* or true if you do an update */ );
                String nom = objCentre.getField("ladRefcforNom").getValue();
                String ville = objCentre.getField("ladRefcforVille").getValue();
                String cp = objCentre.getField("ladRefcforCodepostal").getValue();
                String siret = objCentre.getField("ladRefcforSiret").getValue();
                String adresse = objCentre.getField("ladRefcforAdresse").getValue();

                //String vallibelle = nom + " " + siret + " " + ville + " " + cp + " " + adresse ; // afficher le centre de formation
                String vallibelle = nom + "¤" + siret + "¤" + ville + "¤" + cp + "¤" + adresse ; // afficher le centre de formation
                // Create a pattern from regex
                Pattern pattern = Pattern.compile(".*" + query + ".*", Pattern.CASE_INSENSITIVE);

                // Create a matcher for the input String
                Matcher matcher = pattern.matcher(vallibelle);
                if (matcher.lookingAt()) {
                    list.add(vallibelle);
                }
            }
            return list;

        } catch (Exception e) {
            AppLog.error(LadPmfpUtils.class, "getListeCentre", " - erreur : ", e, null);
        }
      
        return Collections.emptyList();
    }

j’ai lu vos remarques concernant le séparateur, j’implémenterai ça au plus tôt.

Ensuite je coche la case autocomplétion de simplicité (la case oui), et cela me permet de récupérer une liste de suggestion qui puise ces données d’un fichier csv centre-formation importé dans simplicité.

Merci pour votre retour,

Ok c’est le hook back fieldCompletion qui calcule la liste, celui ci doit donc être rapide (appelé souvent). Quelques conseils sur votre code :

A chaque recherche ça va boucler sur toute la table : il faudrait plutôt utiliser des filtres SQL (type like) que Java pour filtrer directement en base

Cet objet peut potentiellement être utilisé par d’autres thread : il faut le synchroniser + vider les filtres éventuels avant de faire un search. Sinon il faut utiliser une instance particulière de session que seul ce traitement utilisera, ex : getObject("ma_completion_xyz", "LadRefCentreFormation")

ObjectDB objCentre = getGrant().getTmpObject("LadRefCentreFormation")
...
String q = Tool.toSQL(query);
synchronized(objCentre) { // objCentre.getLock() suivant votre version
  objCentre.resetFilters();
  objCentre.setSearchSpec("t.col1 like '%"+q+"%' or t.col2 like '%"+q+"%' ...");
  for (String[] row : objCentre.search()) {
    // concat simple
    list.add(vallibelle);
  }
  // remove search spec
  objCentre.setSearchSpec("1=1");
}
...

Si la table est énorme, les like %q% feront un full scan, ce qui n’est pas bon. Il faudra plutot restreindre certaines colonnes (à indexer en base) et qui “commencent par” = like ‘q%’

Si la table est petite et stable, on peut aussi imaginer la mettre en mémoire (faire le search qu’une fois par jour et garder le résultat en mémoire pour ne faire que le “match” java…).

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.