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

<?php
function formproba_menu($may_cache) {
  global
$user;
 
$items = array();

   
$items[] = array(
     
'path' => '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

<?php
function formproba_page(){
 
drupal_add_js(drupal_get_path('module','formproba').'/formproba.js');
  return
drupal_get_form('formproba_form');
}
?>

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

<?php
function formproba_form(){

 
$form['select1'] = array(
   
'#type' => '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

<?php
function formproba_form_submit($form_id, $form_values){
 
drupal_set_message('<pre>'.print_r($form_values,true).'</pre>');
}
?>

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

<?php
function formproba_data(){
  echo
drupal_to_js(formproba_get_select2(arg(2)));
}
?>

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

<?php
function formproba_get_select2($id){
  switch(
$id){
  case
1: return array('1'=>'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 += '<option value="' + i + '">' + j[i] + '</option>';
      }
  $("#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.

CsatolmányMéret
formproba.info76 byte
formproba.js393 byte
formproba.module1.6 KB

Pont valami ilyesmire lett

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

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;

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,

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

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

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

Í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

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

pp

Ezt természetesen megtettem,

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

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

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

Hozzászólás

A mező tartalma nem nyilvános.
  • A webcímek és email címek automatikusan linkekké alakulnak.
  • A sorokat és bekezdéseket a rendszer automatikusan felismeri.
  • You can enable syntax highlighting of source code with the following tags: [code], [blockcode].

További információ a formázási lehetőségekről

Type the characters you see in this picture. (verify using audio)
Type the characters you see in the picture above; if you can't read them, submit the form and a new image will be generated. Not case sensitive.