AJAX - Autocompletion avec scriptaculous
L'une des fonctionnalités natives du client Lotus Notes qui me manque le plus sur le Web est le champ de type "Liste de dialogue" avec l'option "Autoriser valeurs non répertoriées". L'un des moyens de reproduire le comportement de ce type de champ sur le Web est de déclarer deux champs, un de type liste et l'autre de type de texte.
Grace à AJAX et plus particulièrement la bibliothèque de script script.aculo.us, il est possible de créer un tel champ.

Intégration de la librairie script.aculo.us
On pourrait déposer les fichiers Javascript directement sur le serveur Domino. Cependant celà pose plusieurs problèmes. En entreprise, les développeurs n'ont pas directement accès aux serveurs. Il est donc complexe de les mettre à disposition et d'assurer les mises à jour. De plus, il faut que ces fichiers soient présents sur tous les serveurs où cette base sera diffusée sur l'environnement Web (serveur de développement, serveur de recettes, serveur de production, serveur en DMZ, etc.). Il est donc préférable d'intégrer ces fichiers dans une base Notes.

Formulaire de saisie
Pour cet exemple, le formulaire ne contiendra qu'un champ texte libre. Attention, ce champ devra être défini avec un ID afin de pouvoir être exploité par la méthode d'autocomplétion. Pour déclarer un ID sur un champ de saisie il suffit d'ouvrir ses propriétés et d'indiquer un nom dans le champ ID présent dans le dernier onglet.

Pour ajouter la fonction d'autocompletion de script.aculo.us, il faut ajouter le code HTML suivant :

J'utilise les crochets pour indiquer à Domino de ne pas interpréter le code. C'est l'équivalent du HTML relais.
Arguments de la méthode AutoCompleter
new Ajax.Autocompleter("ID du nom du Champ Texte", "ID du div affichant le résultat de la recherche", "URL de l'agent à exécuter pour obtenir le résultat à afficher", {});
Modification de l'entête HTML du formulaire
Pour utiliser l'objet Ajax il va falloir spécifier en entête que l'on intègre les fichiers de la bibliothèque Javascript. On va aussi en profiter pour définir un style pour afficher les résultats dans le div.
Il faut ajouter le code suivant dans la propriété "Contenu de titre HTML" du masque.
Agent de recherche
La méthode AutoCompleter envoi les lettres saisies dans le champ sous form de formulaire encodé (Content-Type: application/x-www-form-urlencoded) avec comme argument &nom_du_champ=texte.
La propriété CGI Request_Content permet de récupérer cet argument. Cependant, d'après la documentation elle ne peut être utilisée qu'à partir d'un agent.
Cet agent va devoir, récupérer la valeur passée en argument, rechercher tous les mots commençant par ces lettres puis renvoyer le résultat sous le format
Pour récupérer la valeur passée en argument j'ai préféré m'appuyer sur une fonction bien utile trouvée sur le forum de Lotus.
Vue de recherche
Pour rechercher les valeurs, nous allons avoir besoin d'une vue triée par ordre croissant sur le nom du champ défini dans le masque. Afin d'éviter d'avoir deux fois le même mot dans le résultat de recherche, il faut cocher l'option "Générer clés uniques dans l'index" depuis le cinquième onglet des options de la vue.

Code de l'agent "search"
L'agent va
Le résultat en action :

Télécharger la base d'exemple : D&W : AJAX - Autocompletion
Grace à AJAX et plus particulièrement la bibliothèque de script script.aculo.us, il est possible de créer un tel champ.

Intégration de la librairie script.aculo.us
On pourrait déposer les fichiers Javascript directement sur le serveur Domino. Cependant celà pose plusieurs problèmes. En entreprise, les développeurs n'ont pas directement accès aux serveurs. Il est donc complexe de les mettre à disposition et d'assurer les mises à jour. De plus, il faut que ces fichiers soient présents sur tous les serveurs où cette base sera diffusée sur l'environnement Web (serveur de développement, serveur de recettes, serveur de production, serveur en DMZ, etc.). Il est donc préférable d'intégrer ces fichiers dans une base Notes.
- Téléchargez le package Javascript su script.aculo.us : http://script.aculo.us/downloads
- Décompressez les fichiers sur le disque dur
- Si vous utilisez une connexion WebDAV, copiez directement le répertoire vers la base depuis l'explorateur
- Si vous n'utilisez pas WebDAV, insérez les fichiers un à un dans les ressources partagées de la base en prenant soin de modifier l'intitulé du fichier en y intégrant le nom du répertoire.

Formulaire de saisie
Pour cet exemple, le formulaire ne contiendra qu'un champ texte libre. Attention, ce champ devra être défini avec un ID afin de pouvoir être exploité par la méthode d'autocomplétion. Pour déclarer un ID sur un champ de saisie il suffit d'ouvrir ses propriétés et d'indiquer un nom dans le champ ID présent dans le dernier onglet.

Pour ajouter la fonction d'autocompletion de script.aculo.us, il faut ajouter le code HTML suivant :
[<div id="autocomplete_choices" class="autocomplete"></div>
<script language="javascript" charset="UTF-8" >
new Ajax.Autocompleter("autocomplete", "autocomplete_choices",
"search?openagent", {});
</script>]

J'utilise les crochets pour indiquer à Domino de ne pas interpréter le code. C'est l'équivalent du HTML relais.
Arguments de la méthode AutoCompleter
new Ajax.Autocompleter("ID du nom du Champ Texte", "ID du div affichant le résultat de la recherche", "URL de l'agent à exécuter pour obtenir le résultat à afficher", {});
Modification de l'entête HTML du formulaire
Pour utiliser l'objet Ajax il va falloir spécifier en entête que l'on intègre les fichiers de la bibliothèque Javascript. On va aussi en profiter pour définir un style pour afficher les résultats dans le div.
Il faut ajouter le code suivant dans la propriété "Contenu de titre HTML" du masque.
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">
<script src=\"scriptaculous/lib/prototype.js\" type=\"text/javascript\"></script>
<script src=\"scriptaculous/src/effects.js\" type=\"text/javascript\"></script>
<script src=\"scriptaculous/src/dragdrop.js\" type=\"text/javascript\"></script>
<script src=\"scriptaculous/src/controls.js\" type=\"text/javascript\"></script>
<style>
div.autocomplete {
font-size:10px;
font-family : tahoma, arial, helvetica, sans-serif;
width: 350px;
background: #fff;
}
div.autocomplete ul {
border:1px solid #888;
margin:0;
padding:0;
width:100%;
list-style-type:none;
}
div.autocomplete ul li.selected {background-color: #ffb;}
div.autocomplete ul li {
margin:0;
padding:3px;
cursor:pointer;
}
</style>"
Agent de recherche
La méthode AutoCompleter envoi les lettres saisies dans le champ sous form de formulaire encodé (Content-Type: application/x-www-form-urlencoded) avec comme argument &nom_du_champ=texte.
La propriété CGI Request_Content permet de récupérer cet argument. Cependant, d'après la documentation elle ne peut être utilisée qu'à partir d'un agent.
Cet agent va devoir, récupérer la valeur passée en argument, rechercher tous les mots commençant par ces lettres puis renvoyer le résultat sous le format
<ul>Function Parse(url, argument)
<li>valeur 1</li>
<li>valeur 2</li>
<li>...</li>
</ul>
Pour récupérer la valeur passée en argument j'ai préféré m'appuyer sur une fonction bien utile trouvée sur le forum de Lotus.
Function Parse( StringToParse As String, ParameterName As String ) As String
Const EqualSign = "="
Const Separator = "&"
Const PlusSign = "+"
Const NotFound = "Not Found"
Dim Found As Long
Dim StartPos As Long
Dim SeparatorPos As Long
Dim ParameterValue As String
Dim lenParameterName As Integer
Dim lenParameter As Integer
Dim InputString As String
Dim lenInput As Long
Dim CharacterCount As Integer
InputString = StringToParse
lenInput = Len( InputString )
ParameterName = ParameterName & "="
lenParameterName = Len( ParameterName )
Found = Instr( 1, InputString, ParameterName )
If Found > 0 Then
StartPos = Found + lenParameterName
SeparatorPos = Instr( Found, InputString, Separator )
If SeparatorPos > Found Then
lenParameter = SeparatorPos - StartPos
For CharacterCount = 1 To Len( ParameterValue )
If Mid$( ParameterValue, CharacterCount, 1 ) = _
PlusSign Then Mid$( ParameterValue, CharacterCount, 1 ) = " "
Next
Else ' Is this the last one?
lenParameter = lenInput - StartPos + 1
End If
ParameterValue = Mid$( InputString, StartPos, lenParameter )
StartPos = SeparatorPos + 1 ' Setup for next parameter search
Else
StartPos = 1 ' Didn't find it, so reset StartPos
End If
Parse = ParameterValue
End Function
Vue de recherche
Pour rechercher les valeurs, nous allons avoir besoin d'une vue triée par ordre croissant sur le nom du champ défini dans le masque. Afin d'éviter d'avoir deux fois le même mot dans le résultat de recherche, il faut cocher l'option "Générer clés uniques dans l'index" depuis le cinquième onglet des options de la vue.

Code de l'agent "search"
L'agent va
- récupérer la valeur envoyée par la méthode AutoCompleter,
- la décoder (la valeur transmise est encodée au format UTF-8),
- rechercher depuis la vue précédemment définie les document dont la valeur du champ commence par cet argument. On utilise l'argument False à la méthode getAllDocumentByKeys.
- renvoyer le résultat sous le format imposé par la méthode AutoCompleter
Sub Initialize
Dim s As New NotesSession
Dim doccur As NotesDocument
Dim db As NotesDatabase
Dim vw As NotesView
Dim dc As NotesDocumentcollection
Dim doc As NotesDocument
Dim valeur As Variant
Set doccur = s.DocumentContext
req_encoded = parse(doccur.Request_Content(0), "autocomplete")
req = Evaluate(|@urldecode({Domino}; {|+req_encoded +|})|)
Set db = s.currentdatabase
Set vw = db.GetView("dbsearch")
Set dc = vw.GetAllDocumentsByKey(req(0), False)
Set doc = dc.GetFirstDocument
Print "Content-Type:text/html;charset=UTF-8"
Print "<ul>"
While Not(doc Is Nothing)
Print "<li>" + doc.autocomplete(0) + "</li>"
Set doc = dc.GetNextDocument(doc)
Wend
Print "</ul>"
End Sub
Le résultat en action :

Télécharger la base d'exemple : D&W : AJAX - Autocompletion
English version

10 Commentaires:
Excelent tip !
C'est spectaculous scriptaculous ;-)
Trés pratique l'option "Générer clés uniques dans l'index" !
Par contre, je limiterai en nombre les résultats retournés par l'agent (10 premiers doc par ex.). Ca risque de plomber les perfs si il ya trop de résultats retourné.
A partir de combien de caractères saisie la recherche se fait ? Dés le premier ?
Par défaut, la recherche se déclenche dès le premier caractère. Cependant il est possible d'ajouter le paramètre optionnel minChar à la méthode Ajax.Autocompleter pour lui indiquer à partir de quand lancer cette recherche.
Pour plus d'info : http://wiki.script.aculo.us/scriptaculous/show/Ajax.Autocompleter
Le script a l'air sympa mais il n'y a aucune explication sur le code server side à utiliser (PHP dans mon cas). Dans le tutoriel, on explique uniquement la partie du code client side (html, javascript, css).
Comment lancer une requete sql quand l'utilisateur saisi une lettre? comment récupérer le texte saisi par l'utilisateur pour filtrer la requète sql ? Et ou placer le code PHP ?
Ah oui très sympa cette fonction ! :)
juste une question pour completer la fonction;
pourquoi après avoir enregistré le formulaire et l'avoir édité à nouveau, la fonction ne marche plus?
Y a t il une solution ou est ce une limite du script?
A première vue je dirai que c'est un problème d'URL relative.
Lorsque le document est créé il a une URL du type http://serveur/base/document alors qu'une fois qu'il est enregistré il a une url de type http://serveur/base/VUE/document
Une solution simple est d'utiliser la balise "BASE href" dans l'entête html de ton masque.
en mettant une balise cela ne marche pas j'ai toujours mon message d'erreur :
'Ajax est indefini'
Merci de ton aide
Remplace le début du contenu HTML de la page :
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">
<script src=\"scriptaculous/lib/prototype.js\" type=\"text/javascript\"></script>
<script src=\"scriptaculous/src/effects.js\" type=\"text/javascript\"></script>
<script src=\"scriptaculous/src/dragdrop.js\" type=\"text/javascript\"></script>
<script src=\"scriptaculous/src/controls.js\" type=\"text/javascript\"></script>
par :
base := @replacesubstring(@subset(@dbname; -1); "\\":" "; "/":"+");
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">
<script src=\"/"+base+"/scriptaculous/lib/prototype.js\" type=\"text/javascript\"></script>
<script src=\"/"+base+"/scriptaculous/src/effects.js\" type=\"text/javascript\"></script>
<script src=\"/"+base+"/scriptaculous/src/dragdrop.js\" type=\"text/javascript\"></script>
<script src=\"/"+base+"/scriptaculous/src/controls.js\" type=\"text/javascript\"></script>
ça y est!
ça fonctionne avec le tag href et quelques autres adaptations
un grand merci pour ton expertise.
SUPER EXEMPLE SUPER BIEN COMMENTE DOCUMENTE !!!!!
Et oui après qqus jours de galère pour implémenter AJAX sous domino, j'ai trouvé cet exemple aujourd'hui même et ENFIN çA MARCHE !!!!
MERCI mille fois !!
Fred
Enregistrer un commentaire
Lien vers ce message:
Créer un lien
<< Home