Problème de selection de villes avec TYPEAHEAD

Autocompletion

a marqué ce sujet comme résolu.

Bonjour à tous

J'ai deux champs text :

  • id="autocomplete-cp" pour le code postal
  • id="autocomplete-villes" pour le nom de la ville

Je veux qu'en tapant le code postal ou le début du nom d'une ville, dans n'importe lequel des 2 champs, ça recherche dans ma BDD et me propose les différentes villes.

pour ce faire je fait :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$(document).ready(function() {


    $('#autocomplete-cp').typeahead({
        remote: './ajax/ajax_villes.php?debut=%QUERY',
        limit: 15
    });


    $('#autocomplete-villes').typeahead({
        remote: './ajax/ajax_villes.php?debut=%QUERY',
        limit: 15
    });

});

ça fonctionne super !

Sauf qu'au final dans mon champ "autocomplete-cp" par exemple, j'ai à la fois le code postale et la ville. Alors que je n'y veux que le code postal. Et de toute façon ça ne me rempli pas l'AUTRE champs.

Donc j'ai un peu bricolé :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$(document).ready(function() {


    $('#autocomplete-cp').typeahead({
        remote: './ajax/ajax_villes.php?debut=%QUERY',
        limit: 15
    });

    $('#autocomplete-cp').on('typeahead:selected', function (e, item) {
        var cp = item['value'].substr(0, 5); //Je sais ça bricole...
        var ville = item['value'].substr(6)
        $('#autocomplete-cp').val(cp); //je force la valeur du champs à "CP" uniquement et pas CP + VILLE
        $('#autocomplete-villes').val(ville);
    });


    $('#autocomplete-villes').typeahead({
        remote: './ajax/ajax_villes.php?debut=%QUERY',
        limit: 15
    });


    $('#autocomplete-villes').on('typeahead:selected', function (e, item) {
        var cp = item['value'].substr(0, 5);
        var ville = item['value'].substr(6)
        $('#autocomplete-cp').val(cp);
        $('#autocomplete-villes').val(ville);
    });

});

ça fonctionne toujours ! quand je valide une proposition :

je tape "75", il me propose "75000 PARIS", je prend, je tape "entrer" ça fait exactement ce que je lui demande : le champs autocomplete-cp prend la valeur du code postal uniquement, et le champs autocomplete-villes, prend la valeur de la ville. Impec !!

Sauf que dés que je "quitte" le champs (pour aller au suivant, numero de téléphone ou autre…) ben mon champ CP reprendre la valeur "75000 Paris" au lieu de "75000" tout court.

et si je quitte le champs "autocomplete-villes" (dans lequel je n'ai rien tapé encore), celui-ci se vide tout seul… j'ai bien tenté un .focusout(), mais la remise en place de la valeur "75000 Paris" se fait APRES le focusout()…

Comment faire pour qu'à tout moment je n'ai que mon code postal dans le champs code postal, et que la ville dans mon champ ville ?

Merci !!

Salut !

J’imagine que tu as regardé s’il n’y avait pas un paramètre qui permettrait de ne pas avoir l’écouteur d’événement sur focusout. Du coup, il te faudrait peut-être le désactiver toi-même dans tes fonctions…

Sinon, il y a quelques années, j’avais créé un petit script qui permettait de faire une recherche de localité et/ou de code postal et remplissait les deux champs depuis l’un des deux, je te le propose ici.

Le système se base sur un script de recherche qui retourne en JSON les champs de la base de données tels quels, mais dont les noms ne correspondent pas nécessairement à ceux des champs du formulaire.

function searchBehaviour() {
    // Adds the behaviour
    $(document).on('keyup', '.ajaxSearch', function(event) {
        var $self = $(this);
        if ($self.val().length >= 3
            && (event.type == 'keyup')
//          && (event.keyCode != 8) // touche d'effacement
            && (event.keyCode != 9) // touche "tabulation"
            && (event.keyCode != 13) // touche "enter"
            && (event.keyCode != 37) // flèche vers la gauche
            && (event.keyCode != 38) // flèche vers le haut
            && (event.keyCode != 39) // flèche vers la droite
            && (event.keyCode != 40) // flèche vers le bas
        ) {
            var postData = {
                q: $self.val()
            }
            var additional;
            if (additional = $self.data('additional')) {
                // Note that retrieving the content of a data-* attribute with
                // $().data() makes it an object or an array.
                $.each(additional, function(name, value) {
                    if ((typeof value === 'string')
                        && (value.match(/^(\.|#|:|\[)/))
                    ) {
                        // We add a value from another field
                        postData[name] = $(value).val();
                    } else {
                        // We add a scalar value 
                        postData[name] = value;
                    }
                });
            }
            var uri = $self.data('uri');
            if (uri.match(/^(\.|#|:|\[)/)) {
                // The URI is based on another element, given by its selector
                uri = $(uri).data('uri') || uri;
            }
            $.ajax({
                type: 'post',
                url: uri,
                data: postData,
                dataType: 'json',
                success: function(data) {
                    if (!$self.next('table.ajaxSearchResult').get(0)) {
                        $('<table>', {
                            "class": 'ajaxSearchResult',
                            "style": 'min-width: ' +  $self.outerWidth() + 'px'
                        }).insertAfter($self);
                    }
                    $self.next('table.ajaxSearchResult').empty();
                    var prototype = $self.data('prototype');
                    $.each(data, function() {
                        var html = prototype;
                        $.each(this, function(name, value) {                            
                            html = html.replace(
                                new RegExp('__' + name + '__', 'g'),
                                value
                            );
                        });
                        if (uri != $self.data('uri')) {
                            html = html.replace(/__([a-z0-9-]+(_[a-z0-9-]+)*?)__/g, '');
                        }
                        html = html.replace(/\s+$/, '').replace(/^\s+/, '');
                        $self.next('table.ajaxSearchResult').append(html);
                    });
                }
            });
        } else if ($self.val().length <= 1
            && (event.keyCode != 9) // touche "tabulation"
            && (event.keyCode != 13) // touche "enter"
            && (event.keyCode != 37) // flèche vers la gauche
            && (event.keyCode != 38) // flèche vers le haut
            && (event.keyCode != 39) // flèche vers la droite
            && (event.keyCode != 40) // flèche vers le bas
        ) {
            $self.next('table.ajaxSearchResult').empty();
        } else if (event.keyCode == 40) { // flèche vers le bas
            var $container = $self.next('table.ajaxSearchResult');
            if (('tr, option', $container).length == 0) {
                return;
            } else if ($('.selected', $container).length == 0 || 
                $('.selected', $container).is($container.find('tr, option').last())
            ) {
                $('.selected', $container).removeClass('selected');
                $('tr, option', $container).first().addClass('selected');
            } else {
                $('.selected', $container).removeClass('selected').next().addClass('selected');
            }
        } else if (event.keyCode == 38) { // flèche vers le haut
            var $container = $self.next('table.ajaxSearchResult');
            if ($('tr, option', $container).length == 0) {
                return;
            } else if ($('.selected', $container).length == 0 || 
                $('.selected', $container).is($container.find('tr, option').first())
            ) {
                $('.selected', $container).removeClass('selected');
                $('tr, option', $container).last().addClass('selected');
            } else {
                $('.selected', $container).removeClass('selected').prev().addClass('selected');
            }
        } else if (event.keyCode = 39
            && $('.selected', $self.next('table.ajaxSearchResult')).length != 0
        ) { // flèche vers la droite
            event.preventDefault();
            $('.selected, .selected *', $self.next('table.ajaxSearchResult')).click();
        }
    });

    // Chooses a proposal
    $(document).on('click', 'table.ajaxSearchResult td', function() {
        var $self = $(this);
        var $container = $self.closest('table.ajaxSearchResult');
        var $input = $container.prev();
        var prototype = $input.data('hidden-prototype');
        // Retrieving other elements to fill
        var inputs = $input.data('others') || [];
        // Adding the current input to an array...
        inputs.push($input);
        // ... to fill each of them in one loop
        $.each(inputs, function() {
            if (typeof this === 'object' && this instanceof jQuery) {
                // By default, we use the HTML content of the row, but sometimes we don't want
                var value = ($self.attr('data-'+this.data('fill') || '')) // NPA starting with 0
                    || ($self.data(this.data('fill') || ''))
                    || $self.data(this.attr('name') || '')
                    || $self.html();
                this.val(value);
            } else {
                // By default, we use the HTML content of the row, but sometimes we don't want
                var value = $self.attr('data-'+$('' + this).data('fill') || '') // NPA starting with 0
                    || $self.data($('' + this).data('fill') || '')
                    || $self.data($('' + this).attr('name') || '')
                    || $self.html();
                $('' + this).val(value);
            }
        });
        if (prototype) {
            var regex = /__([a-z0-9-]+(_[a-z0-9-]+)*?)__/;
            while (matches = regex.exec(prototype)) {
                prototype = prototype.replace(
                    new RegExp(matches[0], 'g'),
                    $(this).data(matches[1])
                );
            };
            // Removing old choices if any, as they'll make a mess
            $container.parent().find(':hidden').remove();
            prototype.replace(/__([a-z0-9-]+(_[a-z0-9-]+)*?)__/g, '');
            $container.after(prototype);
        }
        $container.empty();
    });
    
    // Capture the submit event to cancel it when we choose an item with ENTER
    $(document).on('submit', 'form', function(event) {
        if ($('.ajaxSearchResult .selected', event.target).length) {
            event.preventDefault();
        }
    });

    // Temporary ugly solution to hide search results when focusing on non
    // .ajaxSearch fields
    $(document).on('focus', ':not(.ajaxSearch, .ajaxSearchResult)', function() {
        $('.ajaxSearchResult').empty();
    });
}

$(document).ready(searchBehaviour);
Le fichier JavaScript
<input id="zip" type="text" maxlength="5" class="ajaxSearch" name="zip" placeholder="NPA"
        autocomplete="off"
        data-uri="/locality/searchnpaorlocality"
        <!-- L'URI où envoyer la recherche -->
        data-additional="{&quot;country&quot;:&quot;#id_country&quot;}"
        <!-- Un hash qui renseigne les éventuels autres champs à envoyer quand on cherche depuis celui-ci,
        avec comme clé le nom (attribut name) du champ dans la base de données, et comme valeur
        un sélecteur pour récupérer la valeur supplémentaire -->
        data-others="[&quot;#locality&quot;]"
        <!-- Un tableau de sélecteurs pour les autres champs qui seront remplis avec cette recherche -->
        data-prototype="&lt;tr&gt;&lt;td data-npa=&quot;__npa__&quot; data-locality=&quot;__city__&quot;&gt;__npa__ __city__&lt;/td&gt;&lt;/tr&gt;"
        <!-- Le prototype HTML qui sera utilisé pour générer la liste de résultats. C'est toujours
        une ligne de tableau, avec les données dans la ou les colonnes, mais (et surtout) aussi
        dans les attributs data-*. Le nom qui remplace l'étoile doit correspondre au nom du
        champ dans le formulaire.
        Les données en elles-mêmes sont placées là où il y a les __mon_champ__, où mon_champ
        est le nom du champ dans la base de données. -->
        data-fill="npa"
        <!-- Ça, c'est pour dire que ce champ doit prendre la valeur de data-npa pour être rempli.
        C'est juste parce que mon formulaire sur lequel j'ai utilisé le script n'avait pas les
        champs qui étaient nommés comme en base de données. Si c'est plus logique chez toi,
        tu peux ne pas le mettre. -->
    >
<!-- Le code du champ pour la ville, pour l'exemple -->
<input id="locality" type="text" class="ajaxSearch" name="locality" placeholder="Localité"
        autocomplete="off"
        data-uri="/locality/searchnpaorlocality"
        data-additional="{&quot;country&quot;:&quot;#id_country&quot;}"
        data-others="[&quot;#zip&quot;]"
        data-prototype="&lt;tr&gt;&lt;td data-npa=&quot;__npa__&quot; data-locality=&quot;__city__&quot;&gt;__npa__ __city__&lt;/td&gt;&lt;/tr&gt;"
    >
Un exemple de HTML commenté
/* Search result hint boxes */
.ajaxSearchResult {
    position: absolute;
    margin: 0 !important;
    background-color: #ffffff;
    border: 1px solid black;
    border-radius: 3px;
}
.ajaxSearchResult * {
    border: none !important;
    padding: 2px !important;
}
.ajaxSearchResult *.selected, .ajaxSearchResult *:focus {
    background-color: #3399ff;
    color: #ffffff;
}
Un peu de CSS pour placer les résultats et simuler la sélection au clavier
+0 -0

Salut !

J'imagine que tu as regardé s'il n'y avait pas un paramètre qui permettrait de ne pas avoir l'écouteur d'événement sur focusout. Du coup, il te faudrait peut-être le désactiver toi-même dans tes fonctions…

Sinon, il y a quelques années, j'avais créé un petit script qui permettait de faire une recherche de localité et/ou de code postal et remplissait les deux champs depuis l'un des deux, je te le propose ici.

Je n'ai pas ce niveau là avec jQuery par contre !

En tout merci beaucoup pour ce code ! je vais y jeter un oeil… mais ça m'a l'air tout de même un peu complexe pour le peux de chose qu'il me faut…

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte