Practical symfony

Giorno 18: AJAX

You are currently browsing
the website for symfony 1

Visit the Symfony2 website


About

You are currently reading "Practical symfony" which is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.

Jobeet Links

Master symfony

Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).
trainings.sensiolabs.com

Books on symfony

Learn more about symfony with the official guides.
books.sensiolabs.com

L'audit Qualité par SensioLabs

200 points de contrôle de votre applicatif web.
audit.sensiolabs.com

Chapter Content

Installare jQuery

Includere jQuery

Aggiungere comportamenti

Feedback all'utente

AJAX in un'azione

Testare AJAX

A domani

symfony training
Be trained by symfony experts
Feb 21: Köln (Getting Started with Symfony2 - English)
Feb 27: Köln (Mastering Symfony2 - English)
Mar 05: Köln (Web Development with Symfony2 - Deutsch)
Mar 05: Montreal (Web Development with Symfony2 - English)
Mar 05: Montreal (Getting Started with Symfony2 - English)
and more...

Search


powered by google
You are currently browsing "Practical symfony" in Italian for the 1.4 version - Propel edition - Switch to version: - Switch to ORM: - Switch to language:
Creative Commons License This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Practical symfony (Propel edition)
Support symfony!
Buy this book
or donate.
Buy Practical symfony (Propel edition) from amazon.com
You have been redirected to the symfony 1.4 documentation.
Both symfony 1.3 and symfony 1.4 share the same feature set and documentation.
Read about why we have two different versions.

Ieri abbiamo implementato un motore di ricerca veramente potente per Jobeet, grazie alla libreria Zend Lucene.

Oggi, per migliorare la responsività del motore di ricerca, porteremo i vantaggi di AJAX per avere un motore di ricerca dal vivo.

Siccome i form dovrebbero funzionare anche senza JavaScript abilitato, la ricerca dal vivo sarà implementata usando le tecniche di JavaScript discreto. Usare JavaScript discreto consente anche una migliore separazione tra gli aspetti nel codice del client tra HTML, CSS e comportamenti JavaScript.

Installare jQuery

Invece di reinventare la ruota e gestire i vari differenti browser, useremo una libreria JavaScript, jQuery. Il framework symfony è agnostico e può funzionare con qualsiasi libreria JavaScript.

Andate sul sito di jQuery, scaricate l'ultima versione, e posizionate il file .js sotto web/js/.

Includere jQuery

Poiché jQuery è necessario in tutte le pagine, aggiorniamo il layout per includerlo in <head>. Fate attenzione a inserire la funzione use_javascript() prima della chiamata a include_javascripts():

<!-- apps/frontend/templates/layout.php -->
 
  <?php use_javascript('jquery-1.2.6.min.js') ?>
  <?php include_javascripts() ?>
</head>

Avremmo potuto includere jQuery direttamente con un tag <script>, ma usando l'helper use_javascript() si è sicuri che lo stesso file JavaScript non sia incluso due volte.

Per motivi di prestazioni, si potrebbe voler spostare la chiamata all'helper include_javascripts() subito prima della chiusura del tag </body>.

Aggiungere comportamenti

Implementare una ricerca dal vivo significa che ogni volta che un utente scrive una lettera nel riquadro di ricerca, bisogna attivare una chiamata al server; il server quindi restituirà le informazioni richieste per aggiornare alcune parti della pagina, senza aggiornare la pagina intera.

Invece di aggiungere i comportamenti usando gli attributi HTML on*(), il principio essenziale di jQuery è di aggiungere i comportamenti al DOM dopo che la pagina è stata caricata. In questo modo, se il supporto a JavaScript è disabilitato, nessun comportamento viene registrato e il form funziona come prima.

Il primo passo è intercettare la pressione di un tasto da parte dell'utente nel riquadro di ricerca:

$('#search_keywords').keyup(function(key) {
  if (this.value.length >= 3 || this.value == '')
  {
    // fa qualcosa
  }
});

Non aggiungete codice per il momento, perché lo modificheremo pesantemente. Il codice JavaScript finale sarà aggiunto al layout nella prossima sezione.

Ogni volta che l'utente preme un tasto, jQuery esegue la funzione anonima definita nel codice sopra, ma solo se l'utente ha scritto più di 3 caratteri o se ha rimosso tutto dal tag input.

Fare una chiamata AJAX al server è facile come usare il metodo load() su un elemento DOM:

$('#search_keywords').keyup(function(key) {
  if (this.value.length >= 3 || this.value == '')
  {
    $('#jobs').load(
      $(this).parents('form').attr('action'), { query: this.value + '*' }
    );
  }
});

Per gestire le chiamate AJAX, viene richiamata la stessa azione della chiamata "normale". Le modifiche necessarie nell'azione saranno fatte nella prossima sezione.

Infine, ma non meno importante, se JavaScript è abilitato, rimuoviamo il bottone di ricerca

$('.search input[type="submit"]').hide();

Feedback all'utente

Ogni volta che si fa una chiamata AJAX, la pagina non sarà aggiornata subito. Il browser aspetterà la risposta del server prima di aggiornare la pagina. Nel frattempo, occorre fornire un feedback visuale all'utente, per informarlo che sta succedendo qualcosa.

Una convenzione è quella di mostrare un'icona di caricamento durante la chiamata AJAX. Aggiorniamo il layout per aggiungere l'immagine di caricamento e nasconderla di default:

<!-- apps/frontend/templates/layout.php -->
<div class="search">
  <h2>Ask for a job</h2>
  <form action="<?php echo url_for('job_search') ?>" method="get">
    <input type="text" name="query" value="<?php echo $sf_request->getParameter('query') ?>" id="search_keywords" />
    <input type="submit" value="search" />
    <img id="loader" src="/images/loader.gif" style="vertical-align: middle; display: none" />
    <div class="help">
      Enter some keywords (city, country, position, ...)
    </div>
  </form>
</div>

L'icona di default è ottimizzata per il layout attuale di Jobeet. Se se ne vuole creare uno personalizzato, si possono trovare molti servizi online gratuiti come http://www.ajaxload.info/.

Ora che tutti i pezzi sono a posto, creiamo un file search.js che contenga il codice JavaScript scritto finora:

# web/js/search.js
$(document).ready(function()
{
  $('.search input[type="submit"]').hide();
 
  $('#search_keywords').keyup(function(key)
  {
    if (this.value.length >= 3 || this.value == '')
    {
      $('#loader').show();
      $('#jobs').load(
        $(this).parents('form').attr('action'),
        { query: this.value + '*' },
        function() { $('#loader').hide(); }
      );
    }
  });
});

Occorre anche aggiornare il layout per includere questo nuovo file:

<!-- apps/frontend/templates/layout.php -->
<?php use_javascript('search.js') ?>

AJAX in un'azione

Se JavaScript è abilitato, jQuery intercetterà ogni pressione di tasti nel riquadro della ricerca e richiamerà l'azione search. Altrimenti, la stessa azione search sarà richiamata quando l'utente invierà il form premendo il tasto "invio" o cliccando il bottone "search".

Quindi, l'azione search ora ha bisogno di determinare se la chiamata sia stata fatta tramite AJAX o no. Quando una richiesta viene fatta tramite una chiamata AJAX, il metodo isXmlHttpRequest() dell'oggetto richiesta restituisce true.

Il metodo isXmlHttpRequest() funziona con tutte le principali librerie JavaScript come Prototype, Mootools, o jQuery.

// apps/frontend/modules/job/actions/actions.class.php
public function executeSearch(sfWebRequest $request)
{
  $this->forwardUnless($query = $request->getParameter('query'), 'job', 'index');
 
  $this->jobs = JobeetJobPeer::getForLuceneQuery($query);
 
  if ($request->isXmlHttpRequest())
  {
    return $this->renderPartial('job/list', array('jobs' => $this->jobs));
  }
}

Siccome jQuery non ricarica la pagina, ma si limita a sostituire l'elemento DOM #jobs col contenuto della risposta, la pagina non dovrebbe essere decorata dal layout. Poiché questa è un'esigenza comune, il layout è disabilitato di default quando arriva una richiesta AJAX.

Inoltre, invece di restituire il template pieno, dobbiamo solo restituire il contenuto del partial job/list. Il metodo renderPartial() usato nell'azione restituisce il partial come risposta invece del template pieno.

Se l'utente rimuove tutti i caratteri nel riquadro della ricerca, o se la ricerca non restituisce risultati, dobbiamo mostrare un messaggio al posto di una pagina vuota. Useremo il metodo renderText() per mostrare una semplice stringa di testo.

// apps/frontend/modules/job/actions/actions.class.php
public function executeSearch(sfWebRequest $request)
{
  $this->forwardUnless($query = $request->getParameter('query'), 'job', 'index');
 
  $this->jobs = JobeetJobPeer::getForLuceneQuery($query);
 
  if ($request->isXmlHttpRequest())
  {
    if ('*' == $query || !$this->jobs)
    {
      return $this->renderText('No results.');
    }
 
    return $this->renderPartial('job/list', array('jobs' => $this->jobs));
  }
}

Si può anche restituire un component in un'azione usando il metodo renderComponent().

Testare AJAX

Siccome il browser di symfony non può simulare JavaScript, occorre aiutarlo quando si testano le chiamate AJAX. Ciò significa essenzialmente che occorre aggiungere a mano l'header che jQuery e tutte le altre principali librerie JavaScript inviano con la richiesta:

// test/functional/frontend/jobActionsTest.php
$browser->setHttpHeader('X_REQUESTED_WITH', 'XMLHttpRequest');
$browser->
  info('5 - Live search')->
 
  get('/search?query=sens*')->
  with('response')->begin()->
    checkElement('table tr', 2)->
  end()
;

Il metodo setHttpHeader() imposta un header HTTP per la prossima richiesta fatta col browser.

A domani

Ieri abbiamo usato la libreria Zend Lucene per implementare un motore di ricerca. Oggi abbiamo usato jQuery per renderlo più responsivo. Il framework symfony fornisce tutti gli strumenti fondamentali per costruire un'applicazione MVC con facilità e si comporta bene anche con gli altri componenti. Come sempre, provate a usare lo strumento migliore per le esigenze.

Domani vedremo come internazionalizzare il sito di Jobeet.

Giorno 19: Internazionalizzazione e Localizzazione »
« Giorno 17: Ricerca

Questions & Feedback

If you find a typo or an error, please register and open a ticket.

If you need support or have a technical question, please post to the official user mailing-list.