AJAX vagyis AJAJ a Drupalban

A drupal.hu fórumán tette fel andrew a kérdést, hogy hogyan tudja megoldani a Drupal-lal, hogy egy legördülő lista tartalma egy másik "select" mező változásakor automatikusan frissüljön. A megoldáshoz modul fejlesztési, form API és minimális jQuery ismeretekre lesz szükségünk. A megértést elősegítendő készítettem egy deszkamodellt, hogy a hosszadalmas és ködös magyarázkodás helyett egy konkrét példán keresztül tudjam bemutatni a megoldást.

Az elkészített modul 3 fájlból áll. Az probaform.info fájl a minden modulnál elengedhetetlen információkat tartalmazza. A prbaform.module a php kódot, a probaform.js pedig a JavaScript kódot tartalmazza.

A probaform.module függvényei.

probaform_menu

'formproba', 'callback' => 'formproba_page', 'title' => 'Form próba', 'access' => user_access('access content'), ); $items[] = array( 'path' => 'formproba/data', 'callback' => 'formproba_data', 'access' => user_access('access content'), 'type' => MENU_CALLBACK); return $items; } ?>

Két útvonalat definiálunk. Az egyik (probaform) egy oldal, mely a formot jeleníti meg. A tesztelést megkönnyítendő a menübe is elhelyeztük, csak rá kell kattintani. A második útvonal (probaform/data) mely a JS kérésekre fog válaszolni.

probaform_page

Ez a függvény fogja megjeleníteni a formot, valamint a probaform.js fájlt betöltéséről gondoskodik. Fontos, hogy ne a sminkbe hegesszük bele a .js fájljaink betöltését, hanem a drupal_add_js függvénnyel hívjuk be azokat. A Drupal ugyanis csak akkor fogja beletenni a header részbe a drupal.js és jquery.js hivatkozásokat, ha az szükséges. A drupal_add_js függvénnyel tudjuk jelezni a Drupal-nak, hogy szeretnénk azokat használni.

probaform_form

'select', '#title' => 'Első választás', '#default_value' => 1, '#options' => array(1 => 'gyümölcs',2 => 'növény',3 => 'állat',), ); $form['select2'] = array( '#type' => 'select', '#title' => 'Második választás', '#options' => isset($_POST['select1'])?formproba_get_select2($_POST['select1']):formproba_get_select2(1), ); $form['submit'] = array( '#type' => 'submit', '#value' => 'Elküld', ); return $form; } ?>

A formot leíró kódot tartalmazza. Látható, hogy két select mezőnk van valamit egy submit gombunk. Igazából a második lista (select2) options része szorul magyarázatra és az, hogy miért nem kell használnunk a #DANGEROUS_SKIP_CHECK direktívát. A magyarázat előtt tisztázzunk pár dolgot!

Mi is az a DANGEROUS_SKIP_CHECK nem dokumentált direktíva?

Biztonsági megfontolásból a Drupal csak olyan választásokat fogad el a klienstől, mely a kiküldött, vagyis a szerver oldalon generált lehetőségek között szerepel. Abban az esetben, ha egy választó lista tartalmát nem a szerveren, hanem a kliensen építjük fel akkor a Drupal hibaüzenetet fog adni, ha olyan elemet választottunk ki, ami nem szerepel a lehetőségek között. Nem lehet tudni ugyanis, hogy ez a szabályos működés vagy egy támadási kísérlet eredménye. Ezt a direktívát csak megfelelő körültekintés mellet javasolt használni!

Hogyan működik a Drupal form feldolgozása?

Megnézzük, hogy jött-e adat? Nem jött, akkor kirajzoljuk a formot. Jött adat, akkor ellenőrizzük, hogy helyes-e. Nem helyes adatok esetén újra kirajzoljuk a formot, de a helyes adatokat már megjelenítjük, hogy a felhasználónak ne kelljen azokat újra megadni. Helyes adatok esetén feldolgozzuk a kapott adatokat.

A select2 beviteli mező értékeinek előállításakor megnézzük, hogy jött-e adat, ha jött, akkor a választásnak megfelelő listát töltjük be, ha nem jött akkor az elsőt. Mivel ekkor ugyan az a lista a kliens oldalon és az ellenőrzésnél a Drupal nem fog szólni a nem megfelelő adatok miatt.

formproba_form_submit

'.print_r($form_values,true).''); } ?>

Ez a függvény végzi a form feldolgozását. Jelen esetben csak annyit csinál, hogy kiírja az adatokat.

formproba_data

Ez a függvény végzi a kérések kiszolgálását. Semmi mást nem csinál, mint kiírja a kimenetre a lehetőségeket tartalmazó tömb JSON reprezentációját. Tesztelni úgy lehet, hogy a címsorba beírjuk, hogy ?q=formproba/data/1

formproba_get_select2

'alma','2'=>'körte','3'=>'barack'); case 2: return array('4'=>'fa','5'=>'bokor','6'=>'virág'); case 3: return array('7'=>'nyuszi','8'=>'róka','9'=>'malac'); default: return false; } } ?>

Mivel a lehetőségeket tartalmazó tömbre két helyen is szükségünk van ezért azt egy függvénybe burkoltam.

formproba.js függvényei

// $Id$ if(Drupal.jsEnabled){ $(document).ready(formproba_init); } function formproba_init(){ $('#edit-select1').change(function(){ $.getJSON("/",{q: 'formproba/data/' + $(this).val()},formproba_feldolg); }); } function formproba_feldolg(j){ options = ''; for(i in j){ options += ''; } $("#edit-select2").html(options); }

formproba_init

Ez a függvény az oldal betöltődésekor fut le. Beállítja, hogy a select1 mező megváltozásakor egy AJAJ kérés fusson le. Az adatok feldolgozását, azok megérkezésekor a formproba_feldolg függvény fogja végzi.

formproba_feldolg

A megkapott adatokból elkészítjük a megfelelő HTML kódot és lecseréljük a select2 listáját erre.

Címkék: 
CsatolmányMéret
Binary Data formproba.info76 byte
Plain text icon formproba.js393 byte
Binary Data formproba.module1.6 KB

Hozzászólások

Pont valami ilyesmire lett volna szükségem egy projectnél (csak ott 3 szintű), de sajnos akkoriban nem érkezett válasz a kérdésre, így egy másik rendszert (cakePHP) választottam.

Explorer / Firefox különbség

A megoldás szuperül működik nekem Firefox-ból, de Explorer-ben üres marad a 2. lista. A formproba_data függvény javasolt tesztje (formproba/data/1) az Explorer-ben is rendben lefut.

A probléma a böngészők és beállításaik környékén lehet, vagy esetleg a megoldásban?

Köszönöm,
Doka

A echo drupal_to_js(); exit; helyett nem lenne elég egy drupal_json()? Ha nem, miért nem?

Az exit() tényleg hülyeség, ki is szedtem. Ez a leírás azonban még 5.x Drupalra készült és ott még nem volt drupal_json() függvény. Abban igazad van, hogy ma már így kéne!

pp

Szia!
Akkor tehát úgy kell kinéznie 6-os alatt az említett sornak, hogy
drupal_json(formproba_get_select2(arg(2))); ?

Lehet, hogy iszonyatosan láma kérdés, de mit jelent itt az arg(2)?

Ha megnézed a formproba.js-t akkor láthatod, hogy az ajax kérés úgy néz ki, hogy:

  1. function formproba_init(){
  2. $('#edit-select1').change(function(){
  3. $.getJSON("/",{q: 'formproba/data/' + $(this).val()},formproba_feldolg);
  4. });
  5. }

Látszik, hogy az útvonal az a 'formproba/data/' + $(this).val(). Ez a Drupalban úgy értelmeződik, hogy az arg(0) az a fromproba, az arg(1) az a data és az arg(2) pedig az a szám, amit át akarunk adni. Szóval a rövid válasz az az, hogy a fő kategória azonosítója.

Így már érthető számomra minden, köszönöm szépen a választ.
Viszont még most sem akar működni, ezért ismét a segítségedet kérném, ha nem baj. Az imént említett sorban meghívódik a formproba_feldolg függvény, de nekem nem fut le. Beletettem egy alertet, hogy lássam, mikor hajtódik végre, és arra jutottam, hogy ha nem kap semmilyen paramétert a hívásnál, akkor egyáltalán nem fut le. Ha zárójelben megadok neki valamilyen paramétert (pl. "abc"), akkor bizony lefut, és a select2 mezőmben ott lesznek az új választási lehetőségek ('a', 'b', 'c'). Ez így teljesen jó, most már csak annyi kellene, hogy tudjam neki az ajax kérés eredményét átadni, azaz a JSON reprezentációt dolgoztassam fel vele.
Tudnál segíteni?

A formproba_menu-t teljesen újra kell írni! Hatos alatt azért nem megy.

pp

Ezt természetesen megtettem, nem ez a problémám. Az előző hozzászólásomban leírtam, mi nem működik. Minden más jó, annyi a baj, hogy a formproba_feldolg függvény nem kapja meg az ajax kérés eredményét. Le sem fut a függvény azért, mert várna egy paramétert. Minden más okés, csak ez a baj, ebben biztos vagyok.

Próbálkoztam, és végül most már működik. Módosítanom kellett hozzá az ajaxos hívás sorát, mégpedig így:

  1. $.getJSON('/alkönytáram_neve/formproba_data' + $(this).val(), formproba_feldolg);

Köszönöm a nagyszerű posztot és a segítséget! ;)

formproba_menu-t átírva remekül működik.
Nagyon köszönöm, sokat segített!