Problème de selection de villes avec TYPEAHEAD

Autocompletion

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

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 !!

+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.

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.

  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
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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

 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
31
<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é

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* 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

Evitez qu'on vous dise de les lire : FAQ PHP et Symfony 2Tutoriel WAMP • Cliquez 👍 pour dire merci • Marquez vos sujets résolus

+0 -0
Auteur du sujet

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…

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

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