AHAH Form

 
Caricamento di un form tramite ajax

Nella creazione di form può essere necessario procede con passi successivi in una serie di scelte, questo comporta una perdita di tempo per gli utenti e quindi è molto comodo sfruttare le potenzialità offerte da ajax per effettuare questi passaggi in cascata senza dover andare a ricaricare la pagina.

Andiamo a vedere come effettuare questo usando gli AHAH form di Drupal, che consentono di mantenere sufficientemente sicure le informazioni che transitano ma comunque alleggerendo il carico di lavoro per il server; per capire il risultato che vogliamo ottenere andiamo a vedere questa pagina.

Innanzitutto dovremmo creare un nuovo modulo, di conseguenza andiamo a creare i tre file richiesti (*.info, *.install e *.module) e proseguiamo con la creazione del modulo. Ora ci soffermeremo solo sugli elementi necessari alla creazione di form ahah, quindi tralasceremo tutte le parti "inutili".

Iniziamo creando, usando l'hook_menu, i path necessari; nel nostro caso ne abbiamo due, il rpimo in cui verrà presentato il primo form e il secondo che serve invece a far transitare i dati asincroni. Per fare questo andiamo ad inserire:

<?php
/**
* Implementation of hook_menu().
*/
function testhaha_menu() {
 
$items = array();
 
 
$items['testhaha/form'] = array(
   
'title'            => t('Form HAHA'),
   
'description'      => t('Test form to HAHA'),
   
'page callback'    => 'drupal_get_form',
   
'page arguments'   => array('testhaha_form'),
   
'access arguments' => array('access content'),
  );
 
 
$items['testhaha/js'] = array(
   
'title'            => t('JSON'),
   
'description'      => t('Get data for haha'),
   
'page callback'    => 'testhaha_js',
   
'access arguments' => array('access content'),
   
'type'             => MENU_CALLBACK,
  );
 
  return
$items;
}
?>

come vedete le due voci di menu richiamano due differenti pagine; la prima voce genera un form usando al funzione drupal_get_form, e come argomento usa il form che deve essere generato, mentre la seconda è la funzione che restituirà i dati tramite JSON; da notare che in questo caso si tratta ti una cove di menu di tipo MENU_CALLBACK.

Il secondo passaggio riguarda la generazione del form iniziale, non ci soffermiamo su questo, usando le FAPI ho inserito un paio di select, la prima con tre valori fissi, mentre la seconda con un unico valore di default, mentre gli altri devono essere caricati al volo. Per genrare il nostro form usiamo:

<?php
/**
* Geenrate base FORM.
*/
function testhaha_form() {
 
$form['testselect'] = array(
   
'#type'          => 'select',
   
'#title'         => t('Selection 1'),
   
'#description'   => t('First selection'),
   
'#weight'        => -4,
   
'#options'       => array(
     
'A' => t('Option A'),
     
'B' => t('Option B'),
     
'C' => t('Option C'),
    ),
   
'#ahah'          => array(
     
'path'    => 'testhaha/js',
     
'wrapper' => 'hahatestselect2',
    ),
  );
 
 
$form['testselect2'] = array(
   
'#type'          => 'select',
   
'#title'         => t('Selection 2'),
   
'#description'   => t('Second selection'),
   
'#options'       => array(0 => 'Select options'),
   
'#prefix'        => '<div id="hahatestselect2">',
   
'#suffix'        => '</div>',
  );
 
  return
$form;
}
?>

Focalizziamo la nostra attenzione sul primo eleemnto del form, se facciamo attenzione notiamo che è presente una voce particolare, la voce #ahah che rappresenta un array di dati. Il primo elemento dell'array (path) contiene l'indirizzo da cui verranno caricati i dati, mentre la voce wrapper indica quale sarà l'oggetto su cui verranno inserite le modifiche. Nel nostro caso il secondo elemento è racchiuso, usando prefix e suffix, in un div con un ID specifico, quindi quando verrà sostituito il contenuto del div in realtà verrà sostituito il select vero e proprio.

La funzione che si occupa di caricare i dati, invece, è quella che ci interessa maggiormente; questa funzione è stata divisa per comodità in due parti, vediamo la prima:

<?php
/**
* AJAX Callback function.
*/
function testhaha_js() {
 
 
$id = $_POST['testselect'];
 
 
$form = array(
   
'#type'          => 'select',
   
'#title'         => t('Selection 2 (ahah)'),
   
'#description'   => t('Second selection'),
   
'#options'       => array(
     
'1' => 'Subelement 1 - ' . $id,
     
'2' => 'Subelement 2 - ' . $id,
     
'3' => 'Subelement 3 - ' . $id
   
),
  );
 
 
$output = ahah_field_render($form, 'testselect2');
  print
drupal_to_js(array('data' => $output, 'status' => true));
  exit();
}
?>

In questo caso usando i dati di $_POST viene recuperato il valore dell'elemento che genera la chiamata (all'evento onchange), e usando questa informazione viene creato il nuovo elemento da inserire nel form. In questo caso ci siamom limitati a cambiare i nomi delle voci, ma potrebbe essere possibile andare a caricare dati dal database e fare una serie di operazioni prima di mostrare il risultato.

Una volta creato l'elemento viene richiamata la funzione ahah_field_render, la seconda parte del nostro script che analizzeremo dopo, e dopo questa chiamata viene renderizzato l'elemento del form che verrà inviato tramite AJAX prima di terminarne l'esecuzione.

Prima di proseguire, però, vediamo come funziona un form in Drupal:

  1. Il form generato viene messo in cache prima di inviarlo all'utente.
  2. L'utente reinvia il form e il sistema lo confronta con quello in cache.
  3. Se il form in cache è diverso da quello restituito il sistema se ne accorge e lo rifiuta presupponendo che si tratta di un tentativo di inectjon

La funzione ahah_field_render permette di afre proprio questo, modiicare il form in cache, andando a sostituire il field con nome $name con l'elemento da noi generato:

<?php
/**
* Cambia il form in cache al volo
*/
function ahah_field_render($fields, $name) {
 
// Cambia lo status del form
 
$form_state = array('submitted' => FALSE);
 
 
// Recupera l'id del form
 
$form_build_id = $_POST['form_build_id'];
 
 
// Recupera il form dalla cache
 
$form = form_get_cache($form_build_id, $form_state);
 
 
// Sostituisce il field
 
$form[$name] = $fields;
 
 
// Mette il form modificato in cache
 
form_set_cache($form_build_id, $form, $form_state);
 
 
$form += array(
   
'#post' => $_POST,
   
'#programmed' => FALSE,
  );
 
 
// Rigenera il form
 
$form = form_builder($_POST['form_id'], $form, $form_state);
 
 
// Estrae il field modificato
 
$new_form = $form[$name];
 
 
// Renderizza e restituisce il field modificato
 
return drupal_render($new_form);
}
?>

Se per caso il nostro sistema si limitasse a modificare il form lato client quando viene fatto il submit il form in cache non corrisponderà e verrà rifiutato, per questo ad ogni invio il form deve essere modificato anche nella cache. La funzione è (spero) ben commentata e quindi non richiede specifiche informazioni e credo possa essere ben compresa, in caso di richieste di chiarimenti scrivete pure nei commenti.

In alegato trovate il modulo di test. Sono state volutamente escluse tute le funzioni inutili (salvataggio dati, ....) in modo da concentrarsi sull'obbiettivo principale.

AllegatoDimensione
Modulo testahah1.6 KB

Commenti

Ritratto di Anonimo

Un passo in più

Innanzitutto complimenti per il lavoro che condividi.
Se volessi costruire una serie di (es.) 3 select filtrate a cascata; come dovrei procedere?
Mi basta inserire l'indicazione:

...
      '#ahah'          => array(
        'path'    => 'tutorial/js/tableformahah/' . $i,
        'wrapper' => 'table_ahah_container_' . $i,
...

Grazie.

Ritratto di Anonimo

Grazie

Grazie davvero, mi hai risparmiato un sacco di ricerche e di lavoro !!
Complimenti per la chiarezza.

Ritratto di mavimo

Grazie

sono felice che ti sia servito :)

Ritratto di Anonimo

Due commenti

Il primo è relativo all'articolo: ottimo davvero, ben scritto e molto chiarissimo :-)
Il secondo è relativo al sito: vedo due form per l'inserimento del commento...

Ritratto di mavimo

Grazie, a breve anche un

Grazie, a breve anche un articolo su come mischiare questa tecnica ai form in tabelle ;)

Per i due form di commenti grazie per la segnalazione, correggo immediatamente :)

Invia nuovo commento





  • Elementi HTML permessi: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img> <h2> <h3> <h4>
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • Linee e paragrafi vanno a capo automaticamente.
  • Indirizzi web o e-mail vengono trasformati in link automaticamente

Maggiori informazioni sulle opzioni di formattazione.



Condividi contenuti