Auto completion avec ExtJs 3

Les créateurs de ExtJs se sont sans doute inspirés de language comme le VB pour créer les ComboBox (que l'on retrouve dans le VBA d'Excel). Ce champs pour formulaire ne ressemble que de très loin au Select de l'HTML. Il est généralement couplé à un store qu'il utilise pour accéder aux données et permet de super mise en forme, mais aussi la saisie de texte par l'utilisateur. C'est en utilisant cette capacité alié à un store qui requête le serveur que l'on peut facilement créer un champs type input avec de l'autocompletion. L'exemple sera basé sur la liste des pays du monde.

 

1. Au début il y eu l'HTML

L'html c'est bien et pourquoi devrions nous nous en passer. Dans ExtJS 3 j'utilise toujours des formulaires html sur lesquels je rajoute une couche ExtJs si besoin. La fonction applyToMarkup() n'a pas été codé pour rien.
Nous allons donc démarrer avec un peu d'HTML.

 
       <form onsubmit="formSubmit(this); return false;" >
		<input type="text" name="pays" id="pays" />
		<br style="clear: both;"/>
		<input type="submit" value="Envoyer"/>
	</form>

Même si les ComboBox de ExtJs ressemblent au Select de l'HTML, je l'appliquerai volontairement sur un champs Input. L'évenement onsubmit n'est utile que pour la démonstration.

 

2. Puis Sencha créa ExtJs

Bienvenu dans le monde merveilleux de ExtJs, dans ce framework gigantesque nous allons utiliser 2 composants: un store de type JsonStore qui nous servira à requêter le serveur et à servir les données et une ComboBox qui sera appliqué sur l'input HTML. Avant tout, prenez soin d'avoir chargé la librairie ExtJs dans votre fichier.

 

a. Le store

J'ai choisis d'utiliser un store de type Json car j'aime beaucoup la structure du Json qui permet d'organiser correctement les données et ça facilité de manipulation par le javascript mais aussi par PHP.

 
var monStore = new Ext.data.JsonStore({
    autoDestroy: true,
    url: '/test/listePays',
    root: 'data',
    fields: ['value', 'label']
});
 

Pas beaucoup de configuration, l'autoDestroy à true pour ne pas polluer le navigateur, une URL, le nom de l'élement (noeud en xml) root qui contient les données et le nom des champs à exploiter, ici value et label. Value contiendra l'id de la base de donnée et label le nom du pays. C'est deux champs étant le minimum acceptable par une ComboBox.

 

b. La comboBox

La ComboBox est une petite chose fragile, un rien la rend imprévisible.

 
var combo = new Ext.form.ComboBox({
    store: monStore,
    minChars: 3,
    width: 300,
    hiddenName: 'paysNom',
    valueField: 'value',
    displayField: 'label',
    forceSelection: false,
    loadingText: 'Attends je cherche...',
    hideTrigger: true
});

store: me permet d'attacher mon store à la combobox
minChars: définit le nombre de caractère minimum avant de lancer l'évenement onChange
hiddenName: est le nom du champs qui sera envoyé dans le formulaire. Ne mettez pas le nom de l'input, j'ai déjà essayé et c'était pas très joli.
valueField: est le nom du champs qui contient l'id
displayField: est le nom du champs qui contient le nom qui sera affiché
forceSelection: false permet de saisir un texte qui ne sera pas dans la liste retourné par le  serveur
hideTrigger: true pour cacher la flèche sur le coté et faire croire à un input classique.

Il n'y apas besoin de plus dans la configuration.
Pour appliquer  la ComboBox sur le champs HTML, j'utilise la fonction applyToMarkup() avec une recherche Ext.DomQuery.

 
combo.applyToMarkup(Ext.DomQuery.selectNode('input[id="pays"]'));

 

c. Interactions

Maintenant que tout est en place, il faut ajouter un évenement onChange à la ComboBox pour quelle recharge le store depuis le serveur.

 
combo.on({
	'change': 
	{
		fn: function(pcombo)
		{
			monStore.load();
		}
	}
});

Comme vous pouvez le voir, je ne fais rien d'autre que charger mon store. ExtJs ajoutera automatiquement un paramêtre query à la requête HTTP. Ce paramêtre, envoyé en Post,  contiendra ce que l'utilisateur a saisi dans la ComboBox.
 

 

3. Vous avez dit dynamique?

Il ne reste plus qu'à coder le script PHP qui doit absolument retourner un format Json formatté ainsi:

 
{
    "data":
    [
        {"value":147,"label":"France"}
    ],
    [
        {"value":12,"label":"Angleterre"}
    ]
}

Pour créer ce format il suffit de créer un tableau associatif en PHP et de le passer dans la fonction PHP json_encode() ou d'utiliser la classe Zend_Json_Encoder du Zend Framework si votre config de PHP ne supporte pas la fonction native.
Voici le code complet d'une action Symfony (1.0) qui requete une table Pays contenant la paire Id, Nom.

 
public function executeListePays()
{
	$oCrit = new Criteria();
 
	if($this->hasRequestParameter('query') && $this->getRequestParameter('query'))
		$oCrit->add(PaysPeer::PAYS_NOM, $this->getRequestParameter('query').'%', Criteria::LIKE);
 
	$oCrit->addAscendingOrderByColumn(PaysPeer::PAYS_NOM);
	$pays = PaysPeer::doSelect($oCrit);
 
 
	$aPays = array('data' => array());
	foreach($pays as $oPays)
	{
		$aPays['data'][] = array('value' => $oPays->getPaysId(), 'label' => $oPays->getPaysNom());
	}
 
	sfConfig::set('sf_web_debug', false);
	return $this->renderText(json_encode($aPays));
}

 

Notre ComboBox est maintenant opérationnelle, hormis éventuellement un peu de CSS il n'y a plus rien a ajouter. ExtJs s'occupe pour vous de gérer la création d'un DIV et d'y mettre le contenu du JsonStore.
Reste la soumission du formulaire, vous pouvez faire une soumission simple sans avoir à passer par une fonction Javascript. Le nom du champs sera celui spécifié dans l'attribut hidden de la configuration. Si vous avez sélectionné un pays dans la liste, se sera son ID qui sera retourné sinon le texte saisi par l'utilisateur. Vous pouvez allez plus loin dans la configuration de la ComboBox avec les attributs: forceSelection, hiddenId, queryParam, submitValue.

Il n'y aucun commentaire