symfony
AJAX integration
Compatibilité & Javascript
• JavaScript est un langage client-side– Problème de compatibilité selon le navigateur
• Symfony automatise certaines actions javascripts via des helpers
• Le helper javascript affiche des tags <script> standards• La librairie javascript prototype est intégrée• Les helpers AJAX permettent de rafraîchir une portion de
page, sur un clic de lien, une modification ou une soumisssion de formulaire– Beaucoup d’options disponibles pour un maximum de souplesse
• La librairie JavaScript script.aculo.us, est aussi intégré pour les effets visuels
• La JavaScript Object Notation (JSON) est un standard permettant de faire communiquer le serveur et le script client
Le helper JavaScript de base
<?php use_helper('Javascript') ?> <?php echo javascript_tag("
function foobar() {
... }
") ?>
<script type="text/javascript"> //<![CDATA[
function foobar() {
... }
//]]> </script>
Link_to_function
• Permet d’associer du code javascript a un lien
<?php echo link_to_function('Click me!', "alert('foobar')") ?>
<a href="#" onClick="alert('foobar'); return none;">Click me!</a>
• button_to_function() existe également
• Pour associer du code à une imagelink_to_function(image_tag('myimage'), "alert('foobar')")
Mettre à jour un élément de la page avec JavaScript
• Avec le helper update_element_tag()
<div id="indicator">Data processing beginning</div><?php echo javascript_tag(
update_element_function('indicator', array( 'content' => "<strong>Data processing complete</strong>"
))
) ?>
<div id="indicator">Data processing beginning</div> <?php echo javascript_tag("
document.getElementById("indicator").innerHTML ="<strong>Data processing complete</strong>";
") ?>
Vous ne voyez pas l’intérêt?
// Remove the element before the 'indicator‘// and only if $condition is true
update_element_function('indicator', array( 'action' => $condition ? 'remove' : 'empty', 'position' => 'before',
))
Mode dégradé
• <noscript> contient de l’HTML pour les navigateurs sans javascript
<?php if_javascript(); ?> <p>You have JavaScript enabled.</p>
<?php end_if_javascript(); ?>
<noscript> <p>You don't have JavaScript enabled.</p>
</noscript>
prototype
• La librairie prototype apporte les fonctions dont vous avez toujours rêvées pour manipuler le DOM en JavaScript
node = document.getElementById('elementID'); • Devient
node = $('elementID'); • Ou encore en tableau d’élément
nodes = $('firstDiv', 'secondDiv');
Prototype et les sélecteurs CSS
nodes = document.getElementByClassName('myclass');
• Devient
nodes = $$('.myclass');
• De manière plus général avec les sélecteurs CSS
nodes = $$('body div#main ul li.last img > span.legend');
Protoype II• L’itérateur eachvar vegetables = ['Carrots', 'Lettuce', 'Garlic']; vegetables.each(function(food) { alert('I love ' + food); });
• NB pour faire du prototype stand-alone il faut l’inclure (il est distribué avec symfony)
$prototypeDir = sfConfig::get('sf_prototype_web_dir');
$this->getResponse()->addJavascript($prototypeDir.'/js/prototype'); Ouall: javascripts: [%SF_PROTOTYPE_WEB_DIR%/js/prototype]
• Mais il est inclus automatiquement par les helpers AJAX
Helper AJAX
• Permet de rafraîchir un élément non plus avec du JavaScript, mais avec du code PHP exécuté par le serveur
<div id="myzone"></div> <?php echo javascript_tag(
remote_function( array('update' => 'myzone', 'url' => 'mymodule/myaction',
)) ) ?> • Prototype encapsule l’objet xmlHttpRequest
– Compatibilité entre navigateurs
Interaction AJAX
• Une interaction ajax se décompose en trois parties– L’appel (un lien, bouton, une minuterie, …)– Une action a exécuter côté serveur– Une zone de la page où afficher le résultat de
l’action
• Il est possible d’effectuer des interactions plus complexes, si le résultat de l’action est à exécuter par fonction JavaScript côté client
Les helpers AJAX
• Symfony propose de nombreux helpers AJAX– Ils contiennent tous le mot clé remote– Ils acceptent tous un tableau de
paramètres AJAX – Le code produit est du HTML et non du
JavaScript
Lien AJAX<div id="feedback"></div> <?php echo link_to_remote(
'Delete this post', array(
'update' => 'feedback', 'url' => 'post/delete?id='.$post->getId(),
)) ?>
• Quand un clic sur le lien ‘Delete this post’ rafraîchira la zone ayant pour id feedback avec le contenu renvoyer par l’action delete de l’objet post
Options du liens AJAX
<div id="emails"></div> <?php echo link_to_remote(
image_tag('refresh'), array(
'update' => 'emails', 'url' => '@list_emails',
), array(
'class' => 'ajax_link', )
) ?>
Les formulaires AJAX pure
<div id="item_list"></div> <?php echo form_remote_tag(
array( 'update' => 'item_list', 'url' => 'item/add',
)) ?> <label for="item">Item:</label> <?php echo input_tag('item') ?> <?php echo submit_tag('Add') ?>
</form> • Lors de la soumission du formulaire la zone ayant
pour id item_list est rafraîchie avec le résultat de l’action add de l’objet item
Les formulaires AJAX pure
•Un formulaire AJAX ne peut pas être multipart
•pas d’upload de fichiers
Les formulaires AJAX mix<div id="item_list"></div> <?php echo form_tag('@item_add_regular') ?>
<label for="item">Item:</label><?php echo input_tag('item') ?><?php if_javascript(); ?><?php echo submit_to_remote(
'ajax_submit', 'Add in Ajax', array(
'update' => 'item_list', 'url' => '@item_add',
)) ?> <?php end_if_javascript(); ?> <noscript>
<?php echo submit_tag('Add') ?> </noscript>
</form>
Les forumlaires AJAX mix II
• Le bouton AJAX ne sera visible que si JavaScript est activé
• Si l’utilisateur presse la touche entrée c’est l’action spécifiée dans le formulaire qui sera exécutée– Pas l’action AJAX
Champs de formulaire AJAX
<?php echo form_tag('@item_add_regular') ?><label for="item">Item:</label><?php echo input_tag('item') ?><div id="item_suggestion"></div><?php echo observe_field('item',
array('update' => 'item_suggestion', 'url' => '@item_being_typed',
)) ?> <?php echo submit_tag('Add') ?>
</form>
Champs de formulaire AJAX II
• Le module/action associé à la règle de routage @item_being_typed sera exécuté à chaque fois que l’utilisateur modifie le champs observé (item), sans soumission du formulaire.
• L’action sera capable de récupérer la valeur de item via les paramètres de requêtes
• Il est possible de passer des valeurs autres que celles du champs item, sous forme d’expression JavaScript via le paramètre with
Champs de formulaire AJAX III
<?php echo observe_field('item', array(
'update' => 'item_suggestion', 'url' => '@item_being_typed', 'with' => " 'param=' + value ",
)) ?> • N.B. ce helper ne produit pas de l’HTML,
mais un comportement pour le champs passer en paramètre
• Il est possible de surveiller la modification tous les champs d’un formulaire– observe_form()
Appel périodique de fonctions distantes
<div id="notification"></div> <?php echo periodically_call_remote(
array( 'frequency' => 60, 'update' => 'notification', 'url' => '@watch', 'with' => "'param=' + \$F('mycontent')",
)) ?>• Pratique pour gérer une sauvegarde automatique • Valeur par défaut 10 secondes• Le paramètre with prend du JavaScript en
paramètres et donc des expressions prototypes (fonction $F)
Rafraîchissement d’éléments distincts selon le statut de réponse de l’action
<div id="error"></div> <div id="feedback"></div> <p>Hello, World!</p> <?php echo link_to_remote(
'Delete this post', array(
'update' => array('success' => 'feedback', 'failure' => 'error‘
), 'url' => 'post/delete?id='.$post->getId(),
)) ?> • Seuls les codes d’erreur HTTP (500, 404, et tous les codes en dehors
des 2XX) reverront l’état failure, et pas les actions renvoyant sfView::ERROR. Donc pour qu’une action AJAX renvoie un statut failure, elle doit contenir quelque chose comme $this->getResponse()->setStatusCode(404)
Rafraîchir un élément relativement à un autre
<div id="feedback"></div>
<p>Hello, World!</p>
<?php echo link_to_remote(
'Delete this post',
array(
'update' => 'feedback',
'url' => 'post/delete?id='.$post->getId(),
'position' => 'after',
)) ?>
• Le résultat de l’action post/delete sera insérer après l’élément feedback– Entre le div et le p
• Permet d’accumuler des résultas d’actions
Rafraîchir un élément relativement à un autre II
• La paramètre position peut prendre les valeurs suivantes– before: avant l’élément – after: après l’élément – top: au début début du contenu de
l’élément – bottom: à la fin du contenu de l’élément
Rafraîchir un élément sous condition
• Message de confirmation JavaScript<div id="feedback"></div> <?php echo link_to_remote('Delete this post', array( 'update' => 'feedback', 'url' => 'post/delete?id='.$post->getId(), 'confirm' => 'Are you sure?',)) ?>
• test JavaScript (expression prototype)<div id="feedback"></div> <?php echo link_to_remote('Delete this post', array(
'update' => 'feedback', 'url' => 'post/delete?id='.$post->getId(), 'condition' => "$('elementID') == true",
)) ?>
Spécifier la méthode d’un action AJAX
• La méthode par défaut est POST• La paramètre method permet de définir la
méthode à utiliser<div id="feedback"></div><?php echo link_to_remote(
'Delete this post', array(
'update' => 'feedback', 'url' => 'post/delete?id='.$post->getId(),
'method' => 'get', )) ?>
Autorisation d’exécution de script
• Si une réponse AJAX contient du JavaScript, il ne sera pas exécuté par défaut– Réduit les risques d’attaques
<div id="feedback"></div>
<?php echo link_to_remote('Delete this post',
array(
'update' => 'feedback',
'url' => 'post/delete?id='.$post->getId(),
'script' => true,
)) ?>
Statut d’une action AJAX• before: avant que la requête soit passée • after: Immédiatement après que la requête soit passée et
avant le chargement• loading: quand la réponse distante est chargée par le
navigateur• loaded: quand le navigateur a fini de charger la réponse
distante • interactive: quand l’utilisateur peut interagir avec la
réponse distante, même si le chargement n’est pas fini• success: quand la XMLHttpRequest est terminée et que le
code HTTP est en 2XX • failure: quand la XMLHttpRequest est terminée et que le
code HTTP n’est pas en 2XX • 404: quand la requête renvoie un code 404 • complete: quand la XMLHttpRequest est terminée (après
success ou failure)
Indicateur d’activité d’une action AJAX
<div id="feedback"></div> <div id="indicator">Loading...</div><?php echo link_to_remote('Delete this post',
array( 'update' => 'feedback', 'url' => 'post/delete?id='.$post->getId(),'loading' => "Element.show('indicator')",'complete' => "Element.hide('indicator')",
)) ?>
Effets visuels scriptaculous
• Syntaxe scriptaculous
• Met en surbrillance l’élèment 'my_field' Effect.Highlight('my_field', { startcolor:'#ff99ff', endcolor:'#999999' })
• Fait disparaître un élément par le basEffect.BlindDown('id_of_element')
• Gère une transition sortante sur un élément
Effect.Fade('id_of_element', { transition: Effect.Transitions.wobble })
Helpers d’effets visuels en symfony
• Symfony encapsule les fonctions d’effets visuels scriptaculous dans des helpers
<div id="secret_div" style="display:none">I was here all along!
</div><?php echo link_to_function(
'Show the secret div',visual_effect('appear', 'secret_div')
) ?>
• Effectuera un appel à Effect.Appear('secret_div') • Le helper visual_effect est utilisable dans les
statuts de l’action AJAX• Les effets visuels sont concaténables pour un
même statut
Helpers d’effets visuels en symfony II
<div id="feedback"></div> <div id="indicator" style="display: none">Loading...</div> <?php echo link_to_remote(
'Delete this post', array( 'update' => 'feedback', 'url' => 'post/delete?id='.$post->getId(),'loading' => visual_effect('appear', 'indicator'), 'complete' => visual_effect('fade', 'indicator').
visual_effect('highlight', 'feedback'), )) ?>
JavaScript Object Notation (JSON)
• Format d’échange de données léger• Hash JavaScript utilisé pour décrire les informations
d’un objet• Avantage pour une action AJAX
– Facile à lire en JavaScript– Peut réduire le poids de la réponse
• Si une action AJAX doit renvoyer des données structurées à la page appelante pour un traitement JavaScript ultérieur– JSON est un bon format
• C’est très pratique si par exemple un appel AJAX est censé rafraîchir plusieurs éléments de la page appelante
JavaScript Object Notation (JSON) II
var myJsonData = {"menu": { "id": "file", "value": "File", "popup": {
"menuitem": [ {"value": "New", "onclick":
"CreateNewDoc()"}, {"value": "Open", "onclick": "OpenDoc()"}, {"value": "Close", "onclick": "CloseDoc()"}
] }
}}
JavaScript Object Notation (JSON) - exemple
• Code HTML à rafraîchir<h1 id="title">Basic letter</h1>
<p>Dear <span id="name">name_here</span>,</p>
<p>
Your e-mail was received and
will be answered shortly.
</p>
<p>Sincerely,</p> • Code JSON
[["title", "My basic letter"], ["name", "Mr Brown"]]
JavaScript Object Notation (JSON) – niveau template
<?php echo link_to_remote('Refresh the letter', array( 'url' => 'publishing/refresh', 'complete' => 'updateJSON(request, json)‘
)) ?><?php echo javascript_tag("
function updateJSON(request, json) { var nbElementsInResponse = json.length; for (var i = 0; i < nbElementsInResponse; i++) {
Element.update(json[i][0], json[i][1]); }
} ") ?> • Le paramètre complete a accès à l’en-tête json de la réponse et peut la
passer à une fonction tierce. Cette fonction updateJSON() itère sur l’en-tête JSON et pour chaque élément du tableau, rafraîchit l’élément de nom le premier paramètre avec le contenu du second paramètre
JavaScript Object Notation (JSON) – niveau action
class publishingActions extends sfActions {
public function executeRefresh() {
$output = '[["title", "My basic letter"], ["name", "Mr Brown"]]'; $this->getResponse()->setHttpHeader("X-JSON", '('.$output.')');
return sfView::HEADER_ONLY;
}}• Le protocole HTTP permet de stocker du JSON dans une en-tête de
réponse (limitation de la taille des en-têtes)• JSON est devenu un standard notamment au niveau web service• Depuis PHP 5.2 json_encode() et json_decode() permettent de passer
d’un tableau PHP à du code JSON et inversement
Autocomplètion
<?php echo form_tag('mymodule/myaction') ?> Find an author by name: <?php echo input_auto_complete_tag('author',
'default name', 'author/autocomplete',array('autocomplete' => 'off'), array('use_style' => true) ) ?>
<?php echo submit_tag('Find') ?> </form> • Retour attendu de author/autocomplete<ul>
<li>suggestion1</li><li>suggestion2</li> ...
</ul> • author/autocomplete est appelée à chaque modification du champs
author
Autocomplètion – options
• use_style: permet de styler la réponse automatiquement
• frequency: fréquence d’appel périodique (par défault 0.4s).
• indicator: Id d’un indicateur qui sera montré en attendant que le chargement des suggestions commence et caché avec transition une fois qu’il est fini
• tokens: autorise l’autocompletion incrémentale. Si le paramètre est à false, et que l’utilisateur saisit « jane, george », l’action ne recevra que la valeur 'george'
Drag’n’drop
<ul id="items"> <li id="item_1" class="food">Carrot</li> <?php echo draggable_element('item_1', array('revert' => true)) ?><li id="item_2" class="food">Apple</li><?php echo draggable_element('item_2', array('revert' => true)) ?><li id="item_3" class="food">Orange</li><?php echo draggable_element('item_3', array('revert' => true)) ?>
</ul><div id="cart">
<p>Your cart is empty</p> <p>Drag items here to add them to your cart</p>
</div> <?php echo drop_receiving_element('cart', array(
'url' => 'cart/add', 'accept' => 'food', 'update' => 'cart',
)) ?>
Drag’n’drop - options
• Les helpers doivent être écrits après l’élément auquel il donne le comportement, mais pas forcément juste après …
• Options pour le helper draggable_element()– revert: true, l’élément retournera à sa place une fois relâché. Ce
peut être également un fonction arbitraire appelée quand l’élément est relâché
– ghosting: Clone l’élément et déplace ce clone, en laissant l’élment original en place jusqu’à ce qu’il soit déplacé
– snap: false, aucun quadrillage. Sinon l’élément déplaçable ne put être déplacé que sur une grille d’intervalle x et y, dans ce cas là le paramètre est de la forme xy ou [x,y] ou function(x,y){ return [x,y] }.
• Option pour le helper drop_receiving_element() – accept: chaîne de caractère ou tableau contenant des noms de
classes CSS. L’élément receveur n’acceptera que des éléments déplaçable de cette (ces) classe(s) CSS
– hoverclass: classe CSS class ajoutée quand l’utilisateur déplace un élément déplaçable accepté au dessus de l’élément receveur
Listes triables
<p>What do you like most?</p> <ul id="order">
<li id="item_1" class="sortable">Carrots</li><li id="item_2" class="sortable">Apples</li><li id="item_3" class="sortable">Oranges</li>// Nobody likes Brussel sprouts anyway <li id="item_4">Brussel sprouts</li>
</ul> <div id="feedback"></div> <?php echo sortable_element('order', array(
'url' => 'item/sort', 'update' => 'feedback', 'only' => 'sortable',
)) ?>
Listes triables - options
• Requête associée– POST item/sort HTTP/1.1 order[]=1&order[]=3&order[]=2&_= – La liste entère est passée sous forme de tableau (avec le format
order[$rank]=$id, $rank commence à 0, et l’$id associé est ce qu’il y a après le _ dans la propriété id de chaque element de la liste)
– L’Id de la liste sert de nom au tableau de paramètre• Options pour le helper sortable_element()
– only: chaîne de caractère ou tableau contenant des noms de classes CSS. Seuls les éléments enfants de la liste triable ayant cette (ces) classe(s) pourront être triés
– hoverclass: classe CSS ajouté à l ’élément quand la souris pas au dessus de lui
– overlap: horizontal si les éléments sont affichés en ligne (inline), et vertical (valeur par défaut) pour un affichage un élément par ligne
– tag: si la liste à ordonner n’est pas composer de <li>, on doit définir quel type d’éléments sont à rendre déplaçable (par exemple, div ou dl).
Edit in place
<div id="edit_me">You can edit this text</div> <?php echo input_in_place_editor_tag('edit_me',
'mymodule/myaction', array( 'cols' => 40, 'rows' => 10, )
) ?> • Option pour le helper input_in_place_editor_tag
– cols and rows: la taille du textarea affiché (1 => simple input text)– loadTextURL: l’URI d’une action appelée pour afficher le texte à
éditer. Pratique si le contenu éditable utilise un formatage spécial et que l’utilisateur doit éditer le texte sans ce formatage
– save_text et cancel_text: les texte sur le lien de sauvegarde (par défaut "ok") et celui du lien d’annulation (par défaut "cancel").