Faire de l'édition frontale AJAX avec eZ Publish & Mootools (Partie 2)
- Publié le 27 Septembre 2009
- 6 commentaire(s)
- Catégorie : Technologies Web
Ce billet décrit pas à pas le développement de l'édition frontale dont le fonctionnement est visible sur la vidéo du précédent billet (principe du cliquer / éditer). Le framework JavaScript utilisé est Mootools, dont le fonctionnement est tout à fait similaire à ses principaux concurrents, à savoir : jQuery, Prototype & Script.aculo.us, YUI, Dojo, etc.
Pour ceux qui n'ont jamais utilisés un framework JavaScript, j'en profite pour introduire les objectifs et les fonctionnalités de base que l'on peut trouver dans tous ces frameworks, sans pour autant vous aider à faire un choix... Les critères de choix d'un framework dépendent de bien d'autres critères que leur simple capacités individuelles, comme par exemple :
- Eviter la multiplication des frameworks sur un même site (ce que l'on constate malheureusement un peu partout)
- Rester dans la logique de dépendance d'un framework et de son environnement de développement (YUI pour eZ Publish, Dojo pour Zend, Prototype pour Symfony, etc.)
- Maîtriser un framework correctement, plutôt que 3 frameworks passablement
Introduction aux FrameWorks JavaScript
Un framework JavaScript est écrit en JavaScript, et n'est donc pas plus puissant que le langage lui même. Il s'agit seulement d'un ensemble de librairies et d'extensions du modèle objet qui facilitent l'écriture du code, notamment pour :
- Manipuler le DOM (atteindre, ajouter, modifier ou supprimer des balises et des attributs dans la page)
- Manipuler les évènements (évènements de la souris, du clavier)
- Automatiser des effets complexes (effets de fondus, de Drag & Drop, etc.)
- Faciliter les appels et traitements AJAX
- Faciliter la POO, en proposant une syntaxe plus familière (classes, héritage)
- Masquer certaines incompatibilités de navigateurs
La popularisation de ces frameworks a permis de démocratiser un modèle objet de manipulation du DOM, ainsi qu'une syntaxe grammaticale de sélection de noeuds déclinée du CSS3 Selector (dont voici une liste d'exemple en forme de benchmark pro-Mootools) bien plus lisible et compréhensible que l'API DOM & XPATH du W3C, ce qui génère d'ailleurs des initiatives de portage hors JavaScript comme par exemple : http://code.google.com/p/phpquery/
Spécification de la classe Mootools ajaxwebin
Cette classe a pour vocation de déclarer et d'exécuter le comportement de type "cliquer / éditer" sur un ensemble d'éléments similaires dans la page, comme par exemple :
- Cas 1 : Remplacer sur clic toutes les balises A contenant une classe CSS 'cuvee_invert', par un champs de saisie
- Cas 2 : Remplacer sur clic toutes les balises A, positionnées sous une balise TD et contenant une classe CSS commençant par 'stock_', par une liste de déroulante de 1 à 10
Prérequis dans le xHTML :
Les xHTML propulsés doivent être préparés pour faciliter l'identification des éléments éditables, et des correspondances entre les objets et les attributs cibles. Le motif xHTML est la suivante :
<HTMLElement id="rootID_ContentObjectID" class="MatchName" />
Par exemple concernant le cas 1 :
<a class="cuvee_invert" id="cuveename_2224"></a>
Par exemple concernant le cas 2 :
<td class="td80 stock_3" id="stock_2254"><a href="#">3</a></td>
Voici la liste des paramètres et leurs rôles respectifs :
- ajaxmodule (obligatoire) : Racine du module eZ Publish : ajaxmodule/attributeID/objectID/value
- parentMatch (obligatoire) : Syntaxe CSS3 (transmise en l'état à Mootools dans un sélecteur) qui permet d'atteindre tous les éléments impactés par l'édition frontale
- targetMatch (optionnel) : Balise optionnelle à atteindre, comme enfant des éléments résultat du parentMatch (permet par exemple d'atteindre les balises A, enfants des balises TD)
- styleMatch (optionnel) : Permet de définir un style CSS pour faciliter l'identification des éléments éditables dans la page (bordures, couleurs)
- rootID (obligatoire) : <HTMLElement id="rootID_ContentObjectID" class="MatchName" />
- attributeID (obligatoire) : Permet de transmettre le paramètre dans l'URL ajaxmodule/attributeID/objectID/value
- caption (obligatoire) : Permet d'affecter le masque de saisie personnalisé sur clic, détaillé dans le prochain billet
Instanciation des objets de la classe Mootools ajaxwebin
A noter : les objets instanciés dans la propriété 'caption' seront décrits dans le prochain billet
window.addEvent('domready', function() { var myajaxwebin = new Array(); // Ajout de la gestion des stocks myajaxwebin.push( new ajaxwebin({ ajaxmodule: '/ajaxwebin/action', parentMatch: 'td[class*="stock_"]', targetMatch: 'a', styleMatch: {border: '1px dotted #ff0000', background: '#ffffdd'}, rootID: 'stock_', attributeID: 'stock', caption: ( new ajaxcaptionsSelect({ 'minValue': 0, 'maxValue': 12, 'setStyles': {margin: '0 -10px', border: '1px dotted #000', background: '#ffffdd'} })) })); // Ajout de la gestion des title des cuvées myajaxwebin.push( new ajaxwebin({ ajaxmodule: '/ajaxwebin/action', parentMatch: 'a[class="cuvee_invert"]', styleMatch: {border: '1px dotted #ff0000', background: '#ffffdd'}, rootID: 'cuveename_', attributeID: 'title', caption: ( new ajaxcaptionsInput({ 'setStyles': {border: '1px dotted #000', background: '#ffffdd'} })) })); });
Code commenté de la classe Mootools ajaxwebin
Il est difficile d'expliquer et de décrire l'ensemble du code. Les commentaires devraient être suffisants quant à la compréhension des mécanismes. Cependant voici quelques éléments clés de compréhension :
- Voir la documentation des classes sur Mootools, et notamment l'utilisation du setOptions
- Voir la documentation sur bind qui est essentielle à la construction des mécanismes qui mixent la construction dynamique du DOM et l'ajout dynamique d'évènements. Cette méthode permet de modifier la portée du this, pour définir quelle est la valeur du this dans l'exécution des évènements (clics ou autres)
var ajaxwebin = new Class({ Implements: [Options], options: { ajaxmodule: null, parentMatch: null, targetMatch: null, styleMatch: null, rootID: null, attributeID:null, caption:null, currentselected:null, currentID:null, targetElement:null }, initialize: function(options) { this.setOptions(options); this.process(); }, // Appel AJAX au module eZ Publish, et Mise à jour de l'élément en fonction de la valeur retournée updateAJAX: function(value) { var object_id = this.options.currentID; // Définition de l'URL du module eZ Publish var url = this.options.ajaxmodule + '/' + this.options.attributeID + '/' + object_id + '/' + value; new Request({ method: 'get', url: url, onSuccess: function(responseText, responseXML) { this.options.targetElement.set('html', responseText); }.bind(this) }).send(); }, // Supprime le caption généré disposeCaption: function() { this.options.targetElement.setStyle('display', ''); this.options.currentselected = null; this.options.caption.getElement().dispose(); }, //Remplace la zone cible par le caption attendu setCaption: function(element) { var currentselected = this.options.currentselected; this.options.currentID = element.getProperty('id').replace(this.options.rootID, ''); this.options.attributeID = this.options.attributeID; if (currentselected != element) { //Vérfie si une balise cible enfant a été définie, sinon exploite la balise courante this.options.targetElement = (this.options.targetMatch) ? element.getElement(this.options.targetMatch) : element; // On restore l'élément précent, si 'change' ou 'blur' non provoqué if (currentselected) { var restore_selected = (this.options.targetMatch) ? currentselected.getElement(this.options.targetMatch) : currentselected; restore_selected.setStyle('display', ''); } //On rend invisible l'élement cible, pour pouvoir le remplacer this.options.targetElement.setStyle('display', 'none'); //On affecte le caption xHTML généré avec la valeur de la page this.options.caption.setValue( this.options.targetElement.get('text') ); //On supprime les évènements, par sécurité this.options.caption.getElement().removeEvents(); //On affecte les évènements génériques à tous les captions xHTML this.options.caption.getElement().addEvents({ 'change': function() { //Sur changement, on update les données var value = this.options.caption.getValue(); this.updateAJAX(value); this.disposeCaption() }.bind(this), 'blur': function() { //Sur perte du focus this.disposeCaption() }.bind(this) }); //On injecte le caption après l'élément cible this.options.caption.getElement().inject(this.options.targetElement, 'after'); this.options.currentselected = element; } }, process: function() { //Recherche tous les éléments selon le motif parentMatch, et boucle sur les résultats $$(this.options.parentMatch).each(function(element, index) { var elementID = element.getProperty('id'); //Teste si les éléments possèdents des ID if (elementID && (elementID.indexOf(this.options.rootID)) == 0) { //Neutralise les liens <a>, pour faciliter l'insertion des captions if (element.getProperty('href')) element.setProperty('href', 'javascript:void(0)');; element.getElements('a').each(function(element_a) { element_a.setProperty('href', 'javascript:void(0)'); }); //Ajoute l'évènement de clic sur tous les éléments matché element.addEvents({ 'click': function() { this.setCaption(element); }.bind(this) }); element.setStyles(this.options.styleMatch); } }.bind(this)); } });
- Tags :
- eZ Publish
- Edition frontale
- AJAX
- mootools
- ma cave
- CSS
Que boire avec ce billet ?
Domaine du Paternel - Blanc Sec 2006
| Région : | Provence |
| Appellation : | Cassis |
| Domaine : | Domaine du Paternel |
| Couleur : | |
| Stock : | 0 |
| Notation : | |
| Prix : | 14 € |
| Commentaire(s) : | 2 Commentaire(s) |
Grande déception
Comme d'habitude très bon article, cependant comme d'habitude quelque remarque :
Un des point qui me dérange dans Mootools est le fait que l'on veille changer un langage à protoype en langage à objet, et en perdre l'intérêt majeur à savoir l'héritage dynamique. Cela me rappelle un peu les bibliothèques qui font des classes en C ...
La dépendance de Zend Framework à Dojo n'est que relative, du fait du couplage lache de ce (super) Framework, il existe le même composant que pour Dojo pour JQuery au sein mêm du Framework, et faire un composant similaire pour Mootools ne prend pas énormement de temps.
Comme le Zend Framework est un (super) framework, il dispose aussi de son composant pour manipuler le DOM via des CSS, il répond au doux nom de : Zend_Dom_Query.
Classes contre prototype
Merci pour la précision concernant Zend. J'évoque surtout le couplage culturel et communautaire, qui généralement crée des couples forts dans l'habitude des développeurs (comme PHP & MySql). Si ce couple existe (ce qui n'est peut être pas le cas avec Zend & Dojo), il reste intéressant de rester dans la mouvance afin de profiter des bases de connaissances & des retours d'expériences autour du couplage.
Concernant le modèle objet contre prototype, il ne s'agit que d'un problème d'adoption de syntaxe, puisqu'au final le langage ne se transforme pas et reste sur du prototype. On peut donc étendre dynamiquement la définition de la classe et mixer les 2 principes. Quant à l'intérêt d'abstraire la notion de classe, tous les Frameworks JavaScript ont franchis le cap (pas seulement mootools), et il faut bien avouer que ça a considérablement booster le développement de composants clés en main... peut être que les langages à prototype (que je préfère, si si !) sont trop éloignés des concepts scolaires et déroutent les communautés de développeurs.
eZJscore
Salut,
Article très intéressant cependant est ce que tu ne penses pas que ce serait plus simple en utilisant ezjscore ?
Matthieu
Utiliser eZJScore
Bonne question,
Par défaut c'était relativement rapide à faire avec Mootools (que je connais bien) et un simple module eZ. Cependant ça mérite une comparaison, à tester prochainement ![]()
Edition en front d'un bloc XML
Est ce que tu as implémenté la possibilité d'éditer un bloc XML ?
possibilité d'éditer un bloc XML ?
Nope, mais ca serait faisable, par contre il faudrait modifier quelques éléments de code :
- remplacer les appels AJAX GET en POST
- ajouter un customcaption JavaScript qui charge son xHTML en AJAX (vue spécifique pour le datatype bloc XML)
Je note pour une future évolution,
