28 Fév 2016

#miniTuto Champ input avec Autocompletion en Ajax + cache #astuce #6

Google+ Commentaires Newsletter

Ce 6ème mini tutoriel aura pour sujet un outil très apprécié des utilisateurs pour son gain de temps et que tout développeur Web s’est vu devoir réaliser un jour : L’autocompletion (ou suggestion de champs). Vous connaissez certainement la bibliothèque d’interface Utilisateur Jquery UI, Elle est peut être lourde (plusieurs centaines de ko le .js) mais elle permet de faire pas mal de choses intéressantes (parmi les plus utilisées : drag and drop de div et le calendrier datepicker).

J’expliquerai comment faire de l’autcompletion en faisant appel à une base de données des villes de France (plus de 36000 villes exactements), en maximisant l’expérience utilisateur et en essayant de limiter ses appels par un système astucieux de mise en cache…

Reaaaaady 😃 ?

 

Pré-requis

Pour exécuter ce tutoriel et lancer vos scripts chez vous, il vous faut :

  • Un serveur PHP (local ou hébergement web/serveur dédié)
  • Un serveur MySQL (pareil qu’en haut)
  • Quelques bases en PHP, SQL et JavaScript

 

 

html5-jquery-logo

Partie HTML/JQuery

Notre tutoriel va simplement afficher un simple champ input qui listera des villes correspondants à ce que vous aurez écrit. Il vous faudra donc la librairie JQuery + JQuery UI. Vous pourrez utiliser des url directes, ou si vous utilisez un jour dans un projet l’autocompletion et uniquement ça, vous pourrez toujours télécharger le js avec uniquement l’autocomplete par exemple.

Créez le fichier index.html avec le code suivant :


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
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script><script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
       
       


<style>
            #city-container{display: inline-block;position: relative;vertical-align: middle;width: 360px;}
            #city-container input{width:100%}
            #city-container ul{left:0 !important;right:0 !important;max-height:320px;overflow-y:auto;overflow-x:hidden;}
        </style></pre>
 

<script>/** autocomplete **/</script>

<form> 
<h2>Test Formulaire</h2>
<label for="city">Entrer une ville</label>
<span id="city-container">
<input id="city" name="city" type="text" placeholder="Choisir une ville" />
</span>
<span id="loading" style="display: none;"><i class="fa fa-circle-o-notch fa-spin"></i></span>
<input name="city-hidden" type="hidden" />

</form> 
<pre>

Vous noterez que j’ai tout de même mis un thème JQuery-ui, je vous avez dit que c’était lourd ce truc ! Il existe un thème basique que vous pourrez adapter par rapport au thème de votre site.

Pour le cas du style font-awesome après la balise HTML, ce n’est pas une erreur (enfin une erreur de validation w3c oui ^^), je charge une font après la balise HTML pour optimiser le temps de chargement de ma page web (ça se joue à quelques millisecondes certes). Si vous voulez en savoir plus, rendez-vous sur cette page google-developer.

Maintenant passons au code Jquery que vous placerez à la place de /** autocomplete **/ :


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
<script>
/* initialisation paramètres globaux : */
var cache = {}; /* tableau cache de tous les termes */
var term = null; /* terme renseigné dans le champ input */
var baseUrl = 'https://www.monsite.com/'; /* url du site */
baseUrl = '';

$(document).ready(function() {
    /* city autocomplete */
    $('#city').autocomplete({
        minLength:2, /* nombre de caractères minimaux pour lancer une recherche */
        delay:200, /* delais après la dernière touche appuyée avant de lancer une recherche */
        scrollHeight:320,
        appendTo:'#city-container', /* div ou afficher la liste des résultats, si null, ce sera une div en position fixe avant la fin de </body> */
       
        /* dès qu'une recherche se lance, source est executé, il peut contenir soit un tableau JSON de termes, soit une fonctions qui retournera un résultat */
        source:function(e,t){
            term = e.term; /* récupération du terme renseigné dans l'input */
            if(term in cache){ /* on vérifie que la clé "term" existe dans le tableau "cache", si oui alors on affiche le résultat */
                t(cache[term]);
            }else{ /* sinon on fait une requête ajax vers city.php pour rechercher "term" */
                $('#loading').attr('style','');
                $.ajax({
                    type:'GET',
                    url:'city.php',
                    data:'name='+e.term,
                    dataType:"json",
                    async:true,
                    cache:true,
                    success:function(e){
                        cache[term] = e; /* vide ou non, on stocke la liste des résultats avec en clé "term" */
                        if(!e.length){ /* si aucun résultats, on renvoi un tableau vide pour informer qu'on a rien trouvé */
                            var result = [{
                                label: 'Aucune ville trouvée...',
                                value: null,
                                id: null,
                            }];
                            t(result); /* envoit du résultat à source */
                        }else{ /* sinon on renvoit toute la liste */
                            t($.map(e, function (item){
                                return{
                                    label: item.label,
                                    value: item.value,
                                    id: item.id,
                                }
                            }));  /* envoi du résultat à source avec map() de jQuery (permet d'appliquer une fonction pour tous les éléments d'un tableau */
                        }
                        $('#loading').attr('style','display:none;');
                    }
                });
            }
        },
       
        /* sélection depuis la liste des résultats (flèches ou clic) > ajout du résultat automatique et callback */
        select: function(event, ui) {
            $('form input[name="city-id"]').val(ui.item ? ui.item.id : ''); /* on récupère juste l'id qu'on stocke dans l'autre input */
        },
        open: function() {
            $(this).removeClass("ui-corner-all").addClass("ui-corner-top");
        },
        close: function() {
            $(this).removeClass("ui-corner-top").addClass("ui-corner-all");
        },
    });
});
</script>

La particularité de ce script, c’est que dans « source: », je vérifie si le terme recherché n’existe pas déjà dans un tableau avec sa liste de résultats. Si c’est le cas je charge la liste enregistrée, sinon j’appelle « city.php » et je stocke à son tour les résultats dans le tableau…

 

Partie PHP

Le script PHP va ouvrir une connexion SQL et rechercher le terme envoyé pour lister les noms des communes qui correspondraient dans un tableau JSON. J’utiliserai une connexion PDO car mysql_connect est dorénavant une méthode dépréciée depuis PHP 5.5.0 (il faut utiliser PDO ou mysqli_connect).

Avant tout, il faut s’occuper de la base de données :

  • Créez une nouvelle base de données nommée « france » en utf8_general_ci
  • Créez un utilisateur qui aura accès à la base de données (ou utilisez root).
  • Importez ensuite ce fichier SQL qui contient les 36000 communes. (récupérée sur la base de l’insee).

 

Maintenant créez votre fichier city.php dans le même répertoire qu’index.html :


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
<?php $array = array();
$responseCode = 500; /* si la requête est bien en Ajax et la méthode en GET ... */

if((strtolower(filter_input(INPUT_SERVER, 'HTTP_X_REQUESTED_WITH')) === 'xmlhttprequest') && ($_SERVER['REQUEST_METHOD'] == 'GET')){
 /* on récupère le terme et on le duplique en terme en transformant les espaces en tirets et tirets en espaces (au cas ou) */
 $q = str_replace("''","'",urldecode($_REQUEST['name']));
 $q = strtolower(str_replace("'","''",$q));
 $qTiret = str_replace(' ','-',$q);
 $qSpace = str_replace('-',' ',$q);
 $array = array();
 /* connexion SQL (avec PDO car Mysql_connect sera supprimé dès php 7 :P) */
 $host='localhost';
 $port='3306';
 $database='ma_database';
 $user='identifiant';
 $password='mot_de_passe';
 $connexion = new PDO('mysql:host='.$host.';port='.$port.';dbname='.$database, $user, $password);

 /* creation de la requête SQL */
 $query=$connexion->query('SELECT ts.id, ts.name, ts.postalcode FROM city ts WHERE (ts.name LIKE '%'.$q.'%' OR ts.name LIKE '%'.$qTiret.'%' OR ts.name LIKE '%'.$qSpace.'%' OR ts.postalcode LIKE '%'.$q.'%') ORDER BY ts.name ASC');
 $query->setFetchMode(PDO::FETCH_OBJ);

 /* remplissage du tableau avec les termes récupéré en requete (ou non) */
 while($q = $query->fetch()){
  $name = utf8_encode($q->name);
  $postalcode = utf8_encode($q->postalcode);
  $array[] = array( 'id' => $q->id, 'label' => $name.' ('.$postalcode.')', 'value' => $name.' ('.$postalcode.')', );
 }

 $query->closeCursor();
 //die(print_r($array));
 $responseCode = 200; }

 /* génération réponse JSON */
 http_response_code($responseCode);
 header('Content-Type: application/json');
 echo json_encode($array); ?>

Le code affichera donc soit un tableau vide ou rempli. J’y ai ajouté la réponse HTTP utile http_response_code() qui renverra 200 (= OK) ou 500 (= Error), ce qui permettra de gérer votre réponse partie JavaScript avec $.ajax() au niveau du « success: » et du « error: ».

C’est terminé ! Votre script devrait afficher une jolie liste déroulante sous votre champ input de ville, et stocker l’id dans un champ input hidden (plus pratique de récupérer l’id de la ville à la validation du formulaire ^^).

 

Démo et code source

Testez-vous même le code depuis l’iframe ci-dessous en écrivant les 2 premières lettres d’une ville 😋 ! Pour le code source complet c’est par là : city-autocomplete.zip

Continuez sur le site...

Restez informés des Actus, Tutos et Bons-Plans en suivant BXNXG :

Tags