Strona początkowa

Ajax w jQuery

Obsługa ajaxa w jQuery jest jak zwykle bajecznie prosta. W poniższym artykule poznamy różne techniki i zastosowania tej wybitnej metody :).
Z rozdziału o ajaxie wiemu już czym to "coś" jest i jak to obsłużyć w czystym JS. Wiemy też, że nie jest to najłatwiejszą czy najwygodniejszą rzeczą na świecie (dzięki IE :)). Nie w jquery. Tutaj sprawa wygląda o wiele prościej - o czym przekonamy się za chwilę.

Do najprosztszych metody jQuery służących do obsługi ajaxa należą:

$.get(adres_skryptu, data_wysylana, funkcja_zwrotna)

    $.get(
        "zapisz_do_bazy.php",
        {
            imie: "Johny",
            nazwisko: "Bravo"
        },
        function(dane){
            alert("Dane otrzymane: " + dane);
        }
    );

    //ta sama metoda zapisana ale bardziej upakowana
    $.get("zapisz_do_bazy.php", {imie: "Johny", nazwisko: "Bravo"}, function(dane){alert("Dane otrzymane: " + dane) );
    

Poza adresem do skryptu na serwerze reszta atrybutów jest opcjonalna. Nikt nie każe nam wysyłać ani danych ani wykonywać żadnych akcji po otrzymaniu odpowiedzi z serwera.

Bardzo podobne działanie ma metoda $.post, jednak jak sama nazwa wskazuje wysyła ona dane za pomocą post:

    $.post(
        "zapisz_do_bazy.php",
        {
            imie: "Johny",
            nazwisko: "Bravo"
        },
        function(dane){
            alert("Dane otrzymane: " + dane);
        }
    );
    

Powyższe metody mimo że bardzo proste w użyciu, nie dają nam pełnej kontroli nad połączniem. O wiele lepszym do tego celu jest metoda $.ajax:

    $.ajax({
        type     : "POST",
        url      : "zapisz_do_bazy.php",
        data     : {
                imie : 'Marcin',
                kraj : 'Polska'
        },
        success : function(msg) {
            //ten fragment wykona się po POMYŚLNYM zakończeniu połączenia
            //msg zawiera dane zwrócone z serwera
        },
        complete : function(r) {
            //ten fragment wykona się po ZAKONCZENIU połączenia
            //"r" to przykładowa nazwa zmiennej, która zawiera dane zwrócone z serwera
        },
        error:    function(error) {
            //ten fragment wykona się w przypadku BŁĘDU
        }
    });
    

Tak samo jak w przypadku wcześniejszych metod, jedynym wymaganym atrybutem jest url.

Poza urlem możemy określić dodatkowe atrybuty, wśród których znajdują się między innymi:

W praktyce parametrów możliwych do ustawienia jest o wiele więcej, ale większości z nich na codzień zwyczajnie się nie używa. Warto jednak przyjrzeć się stronie http://jqapi.com/#p=jQuery.ajax i zapoznać się z kilkoma z nich.

    $.ajax({
        type     : "GET",
        url      : "zapisz_do_bazy.php",
        data     : "imie=Marcin&kraj=Polska" //inny sposób przekazywania danych - mniej przyjazny niż poprzedni
        success : function(zwrot) {
            $('#wynik').html(zwrot);
        },
        complete : function(r) {
            $('#loading').hide();
        },
        error: function(err) {
            console.log(err)
        }
    });
    

Jak wspomniałem przed chwilą funkcja $.ajax udostępnia nam zdarzenia związanez obsługą połączenia. Wśród nich wyróżniamy:

  1. success - wywoływane po pomyślnym wysłaniu danych do skryptu
  2. complete - wywoływane po zakończeniu połączenia
  3. error - wywoływane gdy nastąpi błąd w połączeniu
    $("input#start").click(function() {
        $.ajax({
            type: "POST",
            url: "test.php",
            data: {
                imie: 'Marcin',
                wiek: 'super stary :('
            },
            complete: function() {
                    $("#loading").hide();
            },
            success: function(msg) {
                    alert( "Dane zwrotne: " + msg );
            },
            error: function() {
                    alert( "Wystąpił błąd w połączniu :(");
            }
        });
    });
	

Nawiązać połączenie już potrafimy. Obsłużyć jego stan za pomocą odpowiednich zdarzeń także. Przyszedł więc czas na obsługę zwróconych danych.

W powyższych przykładach zwrócone dane będą dostępne w domyślnym typie danych czyli w formie tekstu.
Wszystko fajnie jeżeli zwracamy pojedyńczy wynik. Wypiszemy go, sparsujemy, skonwertujemy na inny typ danych czy przeprowadzimy na nim stosowną operację. Prosta sprawa.

    success: function(ret) {
        var wynik = wynik + parseInt(ret, 10);
        $('#wynik').html(wynik);
    },
    

Problem zacznie się w przypadku zwracania na raz kilku zmiennych.

Rozwiązaniem może być przykazywanie ich w postaci np stringa "Ania|Agnieszka|Marcin", w którym konkretne dane oddzielone są znakiem "|".
Wszystko fajnie, ale natrafiamy na problem. Co by się stało, gdyby nasze imię było jakieś unikalne i miało w sobie znak "|"?
Cześć - nazywam się Jacek|Placek.
Pojawiają się kłopoty. Ale to nie wszystko.

Po odebraniu tak zakodowanych danych, rozdzielimy je na części za pomocą "split" (tak samo jak w przypadku podziału ciasteczek):

    success: function(ret) {
        var dane = ret.split('|');
        for (i=0; i<dane.length; i++) {
            console.log(dane[i]);
        }
    },
    

Ale otrzymany wynik będzie błędny, gdyż Jacek|Placek będzie podzielony na części :(

Właśnie w takich przypadkach najlepiej zastosować profesjonalne podejście i skorzystać z danych typu XML lub JSON. Oba typy danych omówimy poniżej.

XML z wykorzystaniem jQUERY

Tak samo jak DOM, XML też ma postać drzewiastą. Właśnie dlatego jQuery bardzo ułatwia nam obsługę danych podanych nam w takiej postaci. Przejdźmy do meritum.

Aby nasz przykład miał sens, przygotujmy po stronie serwera przykładowy plik php generujący nasz XML:

        <?php        
            header('Content-Type: application/xml; charset=utf-8');
            echo "echo '<?xml version="1.0" encoding="UTF-8"?>';
            echo "<ksiazki>";
        
                echo '<ksiazka nr="215">';
                echo "<tytul>Kubuś Puchatek</tytul>";
                echo "<opis>Misio lubiący miodek</opis>";
                echo "</ksiazka>";
        
                echo '<ksiazka nr="332">';
                echo "<tytul>Mały Książe</tytul>";
                echo "<opis>Przygody małego ogrodnika</opis>";
                echo "</ksiazka>";

                echo '<ksiazka nr="619">';
                echo "<tytul>Przepisy kulinarne</tytul>";
                echo "<opis>Najlepsze przepisy na ciastka z wody</opis>";
                echo "</ksiazka>";

            echo "</ksiazki>";
        ?>
    

Aby pozyskać dane w formacie xml, musimy jQuery powiedzieć, że taki typ danych oczekujemy.
Wystarczy ustawić opcjonalny parametr dataType metody $.ajax:

    $("input#start").click(function() {
        $.ajax({
            type: "POST",
            url: "test.php",
            dataType: "xml",
            success: function(xml) {
                   //...
                   //...
            }
            error: function(xml) {
                alert( "Wystąpił błąd: \n" + xml );
            }
        });
    });
    

W zdarzeniu success dostaniemy dane w formacie xml. Jeżeli zwrócony dokumeny będzie błędny, wtedy wykona się zdarzenie error.

Po zwróceniu danych obsługujemy je w bardzo podobny sposób co dokument DOM:

    success: function(xml) {
        var xml_doc = xml;
        var $ul = $('#lista').empty();
        //pobieramy węzły z dokumentu xml
        var ksiazki = xml_dov.find('ksiazka');

        ksiazki.each(function() {
            var tytul = $(this).find('tytul').text();
            var opis = $(this).find('opis').text();
            var ocena = $(this).attr('ocena');

            var $li = '<li><h3 class="title">'+tytul+'</h3><div class="description">'+opis+</div><div class="note">Ocena: <strong>'+ocena+'</strong></div></li>');
            lista.append($li);
        });
    }
    

JSON z wykorzystaniem jQUERY

Najprzyjemniejszym sposobem przesyłania danych jest zastosowanie typu danych json.
Dla zwykłego zjadacza chleba, który jeszcze super bohaterem nie jest, wejście na powyższy link może nic nie dać. Co to do Jasnej Anielki jest ten JSON? W zasadzie nic szczególnego. To sposób zapisu danych tak, by można było z nich korzystać w jak najprzyjemniejszy/najłatwiejszy sposób.

Tak samo jak w przypadku XML musiamy określić jakiego typu danych oczekujemy - w tym przypadku json.

    $("input#start").click(function() {
        var zmienna1 = $('.jakis_input_1').val(); //wartosc zmienna1 = 1
        var zmienna2 = $('.jakis_input_2').val(); //wartosc zmienna2 = 2
        $.ajax({
            type: "POST",
            url: "test.php",
            dataType : 'json',
            data: {
                dana1 : zmienna1,
                dana2 : zmienna2,
            }
            //tutaj będziemy odbierać dane w postaci json
        });
    });
    

Po wysłaniu danych do skryptu na serwerze będziemy je mieli w postaci zmiennych $_POST['dana1'] i $_POST['dana2']. Teraz w prosty sposób możemy poddać je obróbce i zwrócić do naszego skryptu js. Tym razem zamiast tworzyć dokument XML, stworzymy tablicę, a następnie zakodujemy ją do formatu json. Na szczęście php udostępnia nam metodę json_encode, która zrobi to za nas:

    <?php
        $a = $_POST['dana1'];
        $b = $_POST['dana2'];

        $tablica = array();
        $tablica['wynik_1'] = 'Po obróbce ' + $a + $a; //przykładowe działanie na zmiennej
        $tablica['wynik_2'] = 'Po obróbce ' + $b + $b;

        echo json_encode($tablica); //przekształcamy naszą tablicę na zapis typu json
    ?>
    

Powyższy skrypt zwraca nam: {"wynik_1":0,"wynik_2":0}.

Jak widać został nam zwrócony string z zapsiem obiektu zawierającego nasze zmienne.
W przeszłości aby zamienić taki wynik na obiekt, musieliśmy skorzystać ze znanej metody eval. Było to jednak nie wygodne i nie całkiem bezpieczne.
Na szczęśćie gdy zadeklarujemy chęć skorzystania z json, jQuery wykona za nas całą pracę. Tak więc w naszym skrypcie w rezultacie otrzymujemy obiekt js, który zawiera nasze zmienne jako swoje właściwości :). Wystarczy go teraz obrobić. A właściwie nie musimy go obrabiać, bo zwrócony obiekt jest gotowy do użycia!

    $("input#start").click(function() {
        var zmienna1 = $('.jakis_input_1').val();
        var zmienna2 = $('.jakis_input_2').val();
        $.ajax({
            type: "POST",
            url: "test.php",
            dataType : 'json',
            data: {
                dana1 : zmienna1,
                dana2 : zmienna2,
            },
            success : function(json) {
                alert( json['wynik_1'] + "\n" + json['wynik2']);
            }
        });
    });
    

Zwróćcie uwagę na zastosowanie success, a nie complete. Complete domyślnie nie parsuje jsona, tak więc musielibyśmy zastosować starą metodę z metodą eval(json).

Ot cała filozofia :). Zaintersowanych tematem zapraszam do zadawania pytań.

Dynamiczne selekty z wykorzystaniem JSON

Kilkakrotnie znajomi prosili mnie o przedstawienie im rozwiązania problemu 2 selektów, gdzie jeden z nich ustawia dynamicznie optiony w drugim.
Poniższy przykład pokazuje, jak taki problem rozwiązać z wykorzystaniem json:

        <select class="selekt_1">
            <option value="-1">Wybierz płeć</option>
            <option value="k">Kobiety</option>
            <option value="m">Mężczyźni</option>
        </select>

        <select class="selekt_2" disabled="disabled">
            <option value="-1">---</option>
        </select>
    

Mamy więc 2 selekty. Pierwszy z nich służy do wybrania płci, a drugi będzie służyć do wyboru imienia.
Po wybraniu płci, sprawdzamy czy ma ona jakąś sensowną wartość (czyli jest różna od -1) i wysyłamy ją do serwera. Następnie aktywujemy drugi selekt i za pomocą pętli dynamicznie ustawiamy jego optiony.

Do podpięcia (bind) zdarzenia "change" zastosujemy nowy sposób definicji eventów, który pojawił się w wersji 1.4 jquery. Sposób ten jest bardzo czytelny w przypadku deklaracji kilku typów eventów dla jednego obiektu, a który ma postać:

$(element).bind({
     'click'    : function() {....}
     'change'    : function() {....}
     'mouseover' : function() {....}
});

        $('.selekt_1').bind({
            'change' : function() {
                var wartosc = $(this).find('option:selected').val();   //pobieramy wartość wybranego selekta

                if (wartosc!=-1) {  //jeżeli jest inna niż -1 (czyli jeżeli został wybrany model)
                    $.ajax({
                        type: "POST",
                        url: "test_selekta.php",
                        dataType : 'json',
                        data: {
                            gender : wartosc
                        },
                        success : function(json) {
                            var $selekt_2 = $('.selekt_2');
                            $selekt_2.attr('disabled',false); //aktywujemy 2 selekt
                            $selekt_2.empty(); //czyscimy drugi selekt ze zdarzeń i optionów

                            for(i=0; i<json.length; i++) { //tworzymy optiony
                               $selekt_2.append('<option value="'+json[i]['value']+'">'+json[i]['imie']+'</option>');
                            }

                            //ustawiamy jako wybrany pierwszy option w selekcie 2
                            $selekt_2.find('option').eq(0).attr('selected','selected');
                        },
                        error: function() {
                            alert('wystąpił błąd :)');
                        }
                    })
                }
            }
        });
    

Oczywiście aby powyższy skrypt miał sens, musimy stworzyć skrypt test_selekta.php, który na podstawie przesłanego POST będzie wysyłał do przeglądarki json z odpowiednimi wartościami, na podstawie których tworzymy nowe optiony:

    <?php
        $plec = $_POST['gender'];

        $imiona = Array();

        $imiona['m'] = Array();
        $imiona['m'][0] = Array('imie'=>'Arnold', 'value'=>'1');
        $imiona['m'][1] = Array('imie'=>'Bartek', 'value'=>'2');
        $imiona['m'][2] = Array('imie'=>'Darek', 'value'=>'3');
        $imiona['m'][3] = Array('imie'=>'Kamil', 'value'=>'4');
        $imiona['m'][4] = Array('imie'=>'Michał', 'value'=>'5');

        $imiona['k'] = Array();
        $imiona['k'][0] = Array('imie'=>'Ania', 'value'=>'1');
        $imiona['k'][1] = Array('imie'=>'Beata', 'value'=>'2');
        $imiona['k'][2] = Array('imie'=>'Kamila', 'value'=>'3');
        $imiona['k'][3] = Array('imie'=>'Patrycja', 'value'=>'4');
        $imiona['k'][4] = Array('imie'=>'Zenona', 'value'=>'5');

        echo json_encode($imiona[$plec]);
    ?>
    

Problem z głowy. Oczywiście o wiele praktyczniejsze było bym gdyby nasz skrypt php pobierał dane z bazy, ale to tylko prosty przykład, który zresztą łatwo usprawnić.