Ajax: powiązane pola select

Nieraz w serwisach internetowych widziałeś dynamiczne pola select, które uaktywniały się po wyborze opcji z pierwszego selecta. Jeśli chcesz mieć coś takiego na swojej stronie, to ten wpis jest dla Ciebie. Postaram się tutaj krok po kroku pokazać jak to zrobić.

Zacznijmy od prostego formularza w html-u:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<form>
      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
     <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
      <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2" />
      <title>Prosta strona HTML</title>
      </head>
      <body>
      <form>
        <table width="300" border="0">
          <tr>
            <td width="200">
          <select name="mid" id="mid">
              <option value="">Wybierz markę</option>
              <option value="1">Audi</option>
              <option value="2">BMW</option>
            </select>
          </td>
            <td width="100">
          <select name="model" id="model">
              <option value="">Wybierz model</option>
              <option value="1">A3</option>
              <option value="2">A4</option>
          <option value="2">323</option>
            </select>
          </td>
          </tr>
        </table>
      </form>
      </body>
      </html>

Jak widzimy w powyższym kodzie kiedy wybierzemy daną markę samochodu, dajmy na to BMW, to w modelach mamy do wyboru także modele Audi. W tym tutorialu po wyborze marki będą dostępne modele tylko wybranej marki.

Swój skrypt napiszemy w PHP wykorzystując MySQL. W tym celu musimy utworzyć dwie tabele, pierwszą, w której będziemy trzymać dane dotyczące marek samochodu oraz drugą z odpowiednimi modelami samochodów.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CREATE TABLE `marki` (
`id` INT(9) NOT NULL AUTO_INCREMENT,
`marka` VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin2 ;

INSERT INTO `marki` VALUES(1, 'Audi');
INSERT INTO `marki` VALUES(2, 'BMW');

CREATE TABLE `modele` (
`id` INT(9) NOT NULL AUTO_INCREMENT,
`mid` INT(9) NOT NULL DEFAULT '0',
`model` VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY  (`id`),
KEY `mid` (`mid`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin2 ;

INSERT INTO `modele` VALUES(1, 1, 'A3');
INSERT INTO `modele` VALUES(2, 1, 'A4');
INSERT INTO `modele` VALUES(3, 2, '320');
INSERT INTO `modele` VALUES(4, 2, '323');
INSERT INTO `modele` VALUES(5, 2, '324');

Krótki komentarz, tworzymy powyżej dwie tabele marki i modele, tabela modele jest powiązana z tabelą marki za pomocą klucza mid.

Przyszedł czas na modyfikację kodu HTML i przerobienie go na skrypt php, który nazwiemy samochody.php. Musimy linijki 12-16 przerobić w taki sposób, aby dane były pobierane z bazy danych. A więc fragment:

1
2
3
4
5
<select id="mid" name="mid">
<option>Wybierz markę</option>
 <option value="1">Audi</option>
<option value="2">BMW</option>
 </select>

zamieniamy na:

1
2
3
4
5
6
7
8
9
10
11
12
      <?php
        include("config.php");
        echo "<select name="mid" onchange="ajaxFunction()" id="mid" width="25">"
        ."<option value="">--wybierz--</option>";
        $result2 = mysql_query("SELECT id, marka FROM marki ORDER BY marka");
        while ($row = mysql_fetch_array($result2)) {
          $mid = intval($row['id']);
          $marka = $row['marka'];
          echo"<option value="".$mid."">".$marka."</option>";
        }
        echo"</select><br>";
      ?>

W powyższym kodzie dołączyliśmy jeszcze jeden plik, który odpowiada za połączenie z bazą danych. Plik config.php wygląda następująco:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
      <?php
       
      $sql_host = "localhost";        // host bazy danych, najczesciej localhost
      $sql_user = "tobiasz_arek";  // uzytkownik bazy danych
      $sql_password = "haselko";  // haslo do bazy danych
      $sql_baza = "tobiasz_auto"; // nazwa bazy danych, z ktorej bedzie korzystal portal
      $prefix = "toar";    // prefix uzywany dla tabel korzystajacych z systemu ToAr

      /* Polaczenie z MySQL'em */
      $baza = mysql_connect($sql_host, $sql_user, $sql_password);
        if ($baza) {
      /* Jezeli polaczenie zostalo nawiazane wybieramy baze danych, z ktorej korzysta strona */
          $wynik = mysql_select_db($sql_baza);
        if($wynik) {
        /* narzucamy odpowiednie kodowanie dla clienta i polaczenia */
         mysql_query('SET character_set_connection=latin2');
         mysql_query('SET character_set_client=latin2');
         mysql_query('SET character_set_results=latin2');
        }
      }
      ?>

Po zapisaniu tych dwóch plików i wrzuceniu na serwer nasze marki samochodów powinni się wyświetlać, więc teraz czas na zrobienie dynamicznego generowania modeli. W kodzie selecta jest odwołanie do funkcji ajaxFunction(), dlatego teraz zajmiemy się nią. Na początku w sekcji musimy dodać plik Javascriptu:

1
2
3
4
5
      <head>
      <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2" />
      <title>Prosta strona HTML</title>
      <script language="javascript" type="text/javascript" src="ajax.js"></script>
      </head>

Plik, który musimy stworzyć to ajax.js, jego struktura prezentuje się następująco:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 function ajaxFunction(){
 var ajaxRequest;

try{
 ajaxRequest = new XMLHttpRequest();
 } catch (e){
 try{
 ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
 } catch (e) {
 try{
 ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
 } catch (e){
 alert("Your browser broke!");
 return false;
 }
 }
 }

ajaxRequest.onreadystatechange = function(){
 if(ajaxRequest.readyState == 4){
 var ajaxDisplay = document.getElementById('ajaxDiv');
 ajaxDisplay.innerHTML = ajaxRequest.responseText;
 }
 }
 var mid = document.getElementById('mid').value;
 var queryString = "?mid=" + mid;
 ajaxRequest.open("GET", "modele.php" + queryString, true);
 ajaxRequest.send(null);
 }

W linijkach 4-17 jest tzw. przechwytywanie wyjątków, które wyświetli odpowiedni komunikat jeżeli przeglądarka nie będzie w stanie wykonać skryptu. Kolejne linijki odpowiadają za przechwycenie wartości wybranego pierwszego selecta i wygenerowanie nowego, który znajduje się w pliku modele.php. Skrypt ten pobiera id wybranej marki samochodu za pomocą parametru $_GET[‚mid’]. W związku z tym najpierw w naszym pliku samochodu.php zmienimy kod drugiego selecta na:

1
2
3
      <td width="100">
      <div id='ajaxDiv'></div>
      </td>

W sekcji ajaxDiv po wybraniu marki samochodu pojawią się do wyboru jej modele, ale najpierw musimy stworzyć odpowiedni plik modele.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
      <?php
      $mid = $_GET['mid'];
      if(!empty($mid)) {
        include("config.php");
        $dropdown = "<select name="model"  id="model" width="25">";
        $dropdown .= "<option value="">--wybierz--</option>";

        $result2 = mysql_query("SELECT id, model FROM modele WHERE mid=".$mid." ORDER BY model");

        while ($row = mysql_fetch_array($result2)) {
          $id = intval($row['id']);
          $model = $row['model'];
          $dropdown .= "<option value="".$id."">".$model."</option>";
        }
        $dropdown .= "</select><br>";
      echo $dropdown;
}
      ?>

Jeszcze na wszelki wypadek kod pliku samochody.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
      <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2" />
      <title>Prosta strona HTML</title>
      <script language="javascript" type="text/javascript" src="ajax.js"></script>
      </head>
      <body>
      <form>
        <table width="300" border="0">
          <tr>
            <td width="200">
        <?php
        include("config.php");
        echo "<select name="mid" onchange="ajaxFunction()" id="mid" width="25">"
        ."<option value="">--wybierz--</option>";
        $result2 = mysql_query("SELECT id, marka FROM marki ORDER BY marka");
        while ($row = mysql_fetch_array($result2)) {
          $mid = intval($row['id']);
          $marka = $row['marka'];
          echo"<option value="".$mid."">".$marka."</option>";
        }
        echo"</select><br>";
      ?>
          </td>
            <td width="100">
          <div id='ajaxDiv'></div>
      </td>
          </tr>
        </table>
      </form>
      </body>
      </html>

W taki oto sposób udało się wykonać nam dynamicznego selecta 😉 Mam nadzieję, że nigdzie nie popełniłem błędu, bo miejscami pisałem z głowy.

Comments

Tomasz Olszewski

podany skrypt nie działa

Reply
Tomasz Olszewski

poprawki do skryptu:

HTML / linia 8

>>

AJAX:
var bid = document.getElementById(‚mid’).value;
>>
var mid = document.getElementById(‚mid’).value;

Pozdrawiam

Reply
Arkadiusz Tobiasz

Tak, mój błąd, literówka. Dokonałem kilka poprawek i już powinno wszystko śmigać.

Reply
Mori

Wybacz, bo tylko rzuciłem okiem… Nigdzie nie sprawdzasz stringów od usera? :/ Wstyd! I zła praktyka, bardzo zła.

Używaj mysql_real_escape_string() !

Reply
Arkadiusz Tobiasz

ale to są pola select i user nie może użyć dowolnego ciągu znaków, jest ograniczony tylko i wyłącznie do listy rozwijanej.

Reply
Mori

Wybacz, Arkadiuszu, ale bzdura. Każdy może dowolnie zmodyfikować sobie zawartość POSTa w momencie przesyłania – musi mieć tylko odpowiednie narzędzia. Nie mówiąc o tym, że ja mogę sobie w Operze wyedytować kod Twojej strony, wczytać do lokalnie i mieć w SELECT’cie dowolne rzeczy.

Hej, wrzuć to gdzieś live – takie demo. I np. zapisuj dokładnie string, jaki zostanie przesłany przez SELECT’a. Nie ingeruj w niego, nic… I zobaczymy, czy da się przesłać tylko to, co jest na liście.

Reply
Arkadiusz Tobiasz

Tylko na moim serwerze to nie zadziała, bowiem moje ustawienia PHP mają odblokowaną opcję Magic Quotes, która automatycznie dodaje znacznik prawego ukośnika do apostrofów i cudzysłowów.

Reply
Mori

Tak czy tak – zostało udowodnione, że magic quotes nie wystarcza. Do „zabicia” zapytania wystarczą i inne znaki.

Zresztą… Zaiwanione z opisu funckji na PHP.net:

mysql_real_escape_string() calls MySQL’s library function mysql_real_escape_string, which prepends backslashes to the following characters: \x00, \n, \r, \, ‚, ” and \x1a.

Twój magic_quotes niczym poza ‚ czy ” się nie zajmie… Naprawdę Arku – testowałem, wiem.

Reply
lukas

A widzisz Mori ja właśnie tego szukałem, jeszcze nie zrobiłęm pod siebie ale zaraz próbuje. I nie interesuje mnie zabezpieczenie skryptu. Dopiero się ucze i wole aby był on czytelny na tyle abym mógł zrozumieć zasade działania.
A w momencie kiedy dojde do wprawy zaczne zabezpieczać skrypt.
W końcy w szkole też cię uczyli dodawać od 1+1 a nie od razu jakieś wyrażenia z całkami itd.
Jak dla mnie przydatne info.
A nigdzie nie jest napisane uwaga super bezpieczny skrypt itd.
Pozdr

Reply
Tadek

Bardzo poprosze o wersje live. Tez sie dopiero ucze i nie chce smigac ;/ albo bez wielkiego czarowania co sie po kolei robi tylko gotowe pliki. Pozdrawiam

Reply
lukas

no prykro mi bardzo ale to u mnie nie działa, także szkoda czasu

Reply
Arkadiusz Tobiasz

już poprawiłem, zła nazwa id w kodzie było. Działający skrypt można podejrzeć pod adresem poniżej
http://tobiasz.org/skrypty/ajax/samochody.php

Reply
lukas

teraz śmiga ładnie 😉

Reply
Sisko

A wiecie moze jak zrobic aby wyswietlone w ten sposob dane mozna bylo zapisac do bazy danych za pomoca php?Bo jakos nie moge sobie z tym poradzic. Z gory dzieki za info. Skrypt ogolnie super;)

Reply
Arkadiusz Tobiasz

jak w zwykłym formularzu, czyli po wysłaniu tego za pomocą metody POST
powinniśmy mieć zmienną $_POST[‚model’], która będzie zawierać id danego modelu

Reply
Sisko

Robie to tak i niestety wyskakuje mi ze nie sa wypelnione wszystkie pola czyli tak jakby nie czyta mi tej zmniennej z formularza:/

<?php

//utworzenie nazw zmiennych
$model=$_POST[‚model’];
$marka=$_POST[‚marka’];
$tytul=$_POST[‚tytul’];
$opis=$_POST[‚opis’];
$budzet=$_POST[‚budzet’];
$platnosc=$_POST[‚platnosc’];

if(!$model || !$marka || !$tytul || !$opis || !$budzet || !$platnosc)
{
echo ‚Nie podano wszystkich danych.’
.’Wróć i spróbuj ponowanie.’;
exit;
}

if (!get_magic_quotes_gpc())
{
$model = addslashes($model);
$marka = addslashes($marka);
$tytul = addslashes($tytul);
$opis = addslashes($opis);
$budzet = addslashes($budzet);
$platnosc = addslashes($platnosc);
}
@$db = new mysqli(‚localhost’ , ‚root’ , ‚vertrigo’ , ‚ogloszenia’);

if (mysqli_connect_errno())
{
echo ‚Błąd: : Połączenie z bazą danych nie powiodło się.’;
exit;
}

$zapytanie = insert into ogloszenia (kategoriaid, tytul , opis , budzet , platnosc) values (‚.$model.’ , ‚.$marka.’ , ‚.$tytul.’ , ‚.$opis.’ , ‚.$budzet.’ , ‚.$platnosc.’);
$wynik = $db->query($zapytanie);
if ($wynik)
echo $db->affected_rows.’ogłoszenie dodano do bazy.’;
?>

Reply
Arkadiusz Tobiasz

a formularz, ja korzystałem z tego przy robieniu komercyjnego portalu i wszystko śmigało jak trzeba 😉

Reply
Piter

Jak później pobrać dane value z drugiego selecta?
$_POST zwraca mi wypisane wartości, a nie value.

Proszę o odpowiedź na maila.

Reply
Arkadiusz Tobiasz

zmienna $_POST[‚model’] zwraca wartość drugiego selecta

Reply
robbys

Niestety nie działa 🙁 Pojawia się takie ostrzerzenie po poworcie do „wybierz” w pierwszym polu wyboru. Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in /home/tobiasz/domains/tobiasz.org/public_html/skrypty/ajax/modele.php on line 10

Reply
Arkadiusz Tobiasz

wystarczy dodać warunek

if(!empty($mid)) {

poprawiłem kod pliku modele.php

Reply
widaw

A jak zrobić to samo tylko z trzema polami?

Reply
Ajax: 3 powiązane pola select | Arkadiusz Tobiasz

[…] pola select, a nawet więcej. Tutorial ten będzie kontynuacją opublikowanego ponad rok temu wpisu Ajax: powiązane pola select, więc zanim przystąpisz do czytania dalszej części wpisu wykonaj wszystkie rzeczy w podanym […]

Reply
robbys

Jak zauważyłem brak ponumerowanych linków z kontynuacją tematu. W poprzedniej wersji bloga występowały. Szkoda 🙁

Reply
Arkadiusz Tobiasz

dzięki, już poprawiłem 😉

Reply
jQuery: powiązane pola select | Arkadiusz Tobiasz

[…] czas temu napisałem jak można powiązać ze sobą dwa pola select, tak aby drugi generował się dynamicznie na podstawie wyboru dokonanego w pierwszym polu select. […]

Reply
radek

Witam,

Jak zrobić żeby formularz pamiętał wybór z pierwszego selecta ? Po zatwierdzeniu tego formularza drugi select znika a w pierwszym należy ponownie wybrać opcję. Jak zrobić żeby pierwszy select pamiętał wybrana opcję ?

Reply
    Arkadiusz Tobiasz

    zatwierdzeniu, czyli wysłaniu? Trzeba odczytać wartość wysłanego pola formularza i porównać z wartościami kolejnych opcji selecta, w przypadku, gdy się pokrywają dodać atrybut selected. Fragment przykładowego kodu:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    .'<select name="deliverer_id" onchange="this.form.submit();">'
        .'<option value="0">--wybierz--</option>';
        $delivererdata = Deliverer::find_all();
        $count = Deliverer::count_all();
        if($count > 0) {
            for($i=0; $i<$count; $i++) {
                if(isset($_POST['deliverer_id']) && $_POST['deliverer_id'] == $delivererdata[$i]->deliverer_id) {
                    echo '<option value="'.$delivererdata[$i]->deliverer_id.'" selected>'.$delivererdata[$i]->name.'</option>';
                } else {
                    echo '<option value="'.$delivererdata[$i]->deliverer_id.'">'.$delivererdata[$i]->name.'</option>';
                }
            }
        }
    echo '</select>'
    Reply
Luka

Nie działają mi polskie znaki. Czy wam też nie?

Reply
Artur

Witam mam Problem spodobal mi sie ten projek wiec postanowilem wykozystac tylko niestety mam taki problem, select dziala w IE zas w FF i Opra Google Chrom Mozilla — slelekt dziala, tylko w formularzu po wybraniu np marki nastpenie modelu auta wyslajac formularz nie wysla modelu. model jest pusty. i wbazie nie ma zapisanego modelu

co moze byc powodem, nie wiem czy jasno opisalem ale bardzo prosze o pomoc co mam zrobic by w tych przegladarkach wykozystujac ten skrypt dalo sie wyslac formularzem ten wybor

Reply
    Arkadiusz Tobiasz

    podejrzewam, że problemem może być połączenie z bazą danych, czy w pliku modele.php jest zrobione połączenie z bazą danych?

    Reply
Ardo

Mam pytanie czy ten skrypt dziala pod FF, Opera Google Chrome, bo u mnie nie, nie mam na mysli dzialnie wstylu wyciagnie z bazy danych, a za pomoca tego skryptu i umieszczeniu go w formularzu, czy dziala przesylanie zapytania, sprwadzalem na prztkladowym takim prostym formularzu dodajac ten wybor co tu jest napisany i wynika tak z tego doswiadczenia, pod IE dziala zas pod FF, Mozilla Opera Chrome juz nie, Obiawy sa takie widac dane pobrane z bazy ale wysylanie ich z formularza nie dziala, dane nie sa wyslane np jako model = IBIZA to formularz wysla model=”” czyli pusty

czy tak jest a moze cos nalezy zminic by to dzialalo prosze o opinie na ten temat.

Reply
    Arkadiusz Tobiasz

    dane są dobrze wysyłane, zastosowałem to rozwiązanie w kilkunastu serwisach i niezależnie od przeglądarki dane są wysyłane, oczywiście nalezy je odczytać jako $_POST[‚mid’] czy $_POST[‚model’]

    Reply
Miako

Czy mozna prosić o pełny skrypt wraz z tym:
„.”
.’–wybierz–‚;
$delivererdata = Deliverer::find_all();
$count = Deliverer::count_all();
if($count > 0) {
for($i=0; $i<$count
…."

jestem poczatkujacy i nie bardzo wiem jak to mam polaczyc do Pana skryptu co jest wyzej by działalo prosze o pomoc w tej sprawie, co musi zawierac plik modele.php bo rozumie ze ten plik ma byc zmodyfikowany. Prosze o pomoc w tej sprawie.

zgory dziekuje

Reply
    Arkadiusz Tobiasz

    Plik odwołuje się do klasy Deliverer i to przykład z jednego skryptu jaki wykonywałem na zlecenie. Nie widzę potrzeby jego zamieszczania. Odpowiada on za wyświetlenie w pętli po kolei wszystkich dostępnych dostawców jako kolejne opcje selecta. W pliku modele.php należy umieścić w pętli kolejne opcje selecta, najczęściej robi się to poprzez wyciągnięcie ich w bazy i prezentację za pomocą pętli for() lub while()

    Reply
Marko

Czy mozna liczyc na pomoc ?

–> Jak zrobić żeby formularz pamiętał wybór z pierwszego selecta ?

czy mozna prosić o przykladowy skrypt jak to wyglada w praktyce ?
odpowiedz jest ale nie wiem jak to ma być połączone z skryptem głównym

Reply

Leave a Reply to robbys Anuluj pisanie odpowiedzi

16 − thirteen =