Mootools 1.1 - XHR.js
25 czerwca, 2007
W kursie mootools przebrnęliśmy już przez 5 z 9 grup plików. Do tej pory poznawaliśmy jednak z reguły rozwiązania, jakie daje nam mootools w zakresie programowania obiektowego, obsługi podstawowych obiektów JavaScript (window, string czy array). Obecnie zajmiemy się moim zdaniem tą fajniejszą częścią frameworka mootools. Przed nami sekcje Remote, Effects, Drag i Plugins - to one sprawią, że nasze strony naprawdę "ożyją" ;) Oczywiście zaczynamy od sekcji Remote - poznamy obsługę AJAX'a, pobawimy się cookies, pododajemy dynamicznie pliki do naszych stron, a na koniec zgłębimy trochę tematykę przesyłu danych w formacie JSON.
Zaczynamy od pliku XHR.js , który z pewnością wielu osobom skojarzy się z obiektem XMLHttpRequest - można powiedzieć sercem AJAX'a, który zmienił znacząco oblicze Internetu - wystarczy spojrzeć na aplikacje webowe Google - GMail, Google Maps czy Google Reader w których AJAX jest wykorzystywany na szeroką skalę.
Plik XHR.js zawiera podstawową obsługę wysyłania i odbierania zapytań. Dodatkowa funkcjonalność została zaimplementowana w plikach Ajax.js i Json.Remote.js, ale by móc je omówić trzeba znać obsługę rozwiązań jakie udostępnia nam plik XHR.js.
Klasa XHR posiada 3 metody oraz 5 parametrów jakie możemy jej ustawić.
Wspomniane parametry to:
- method - określa metodę zapytania : GET lub POST. Wartości zapisujemy jako 'get' lub 'post' przy czym 'post' jest wartością domyślną i w wypadku wywoływania zapytań metodą POST nie musimy zapisywać tego parametru.
- async - jest to wartość logiczna, która określa rodzaj zapytań - asynchroniczne w wypadku wartości logicznej true i synchroniczne gdy parametr ten jest ustawiony jako wartość logiczna false. Jaka jest różnica pomiędzy zapytaniami synchronicznymi i asynchronicznymi ? Żądania asynchroniczne są wywoływane "w tle", a zatem w czasie ich wykonywania można dalej normalnie korzystać ze strony do czasu otrzymania żądania zwrotnego z rezultatami dla wysłanego zapytania. Synchroniczne żądania oznaczają z reguły (w zależności od czasu trwania zapytania) wstrzymanie pracy interfejsu użytkownika do czasu otrzymania odpowiedzi. Domyślna wartość tego parametru to oczywiście true.
-
encoding - parametr ten teoretycznie służy do określania kodowania wysyłanych danych. Domyślna wartość to 'utf-8' oznaczająca oczywiście, że wysyłane dane będą używały kodowania UTF-8. W praktyce nie zauważyłem by ten parametr coś zmieniał. O wiele poważniejszy problem występuje przy odbiorze danych wykorzystujących kodowanie inne niż UTF-8 - wtedy tzw."krzaczki" są gwarantowane ;] Jak to można rozwiązać ? Z pewnością od strony PHP łatwiej bo wystarczy w generowanym pliku wykorzystać funkcję header z odpowiednimi parametrami. Gdybyśmy chcieli to zrobić od strony JS to może być problem. Jest funkcja, która wymusza dany typ dokumentu - overrideMimeType, którą musimy koniecznie wywołać przed wysłaniem zapytania ( przed metodą send() ), ale jest mały szczególik - metoda ta działa tylko (przynajmniej z tego co testowałem) na Firefoxie i Safari - Opera nie reaguje zupełnie (żadnej zmiany ani błędu w konsoli), a IE zwraca błąd. Jakby jednak ktoś chciał użyć z mootools tej funkcji to przykładowy kod prezentuję poniżej - pozwala on na poprawne wyświetlenie odpowiedzi ze strony kodowanej w windows-1250, oczywiście pod warunkiem, że strona na jakiej ją wyświetlamy też używa tego kodowania:
obiekt_XHR.transport.overrideMimeType('text/html;charset=windows-1250'); - autoCancel - jest to parametr, który określa czy zapytanie ma być anulowane, jeżeli w momencie jego wysyłania inne zapytanie było uruchomione. Wartością tego parametru jest wartość logiczna - true lub false. Domyślnie ta opcja jest wyłączona (ma wartość false).
-
headers - to parametr, który jest obiektem zawierającym nagłówki wysyłane wraz z zapytaniem. Nagłówki w tym obiekcie zapisujemy w postaci:
'nazwa_naglowka':'zawartosc_naglowka'Domyślnie obiekt ten jest pusty.
Zanim przejdziemy do praktyki, zapoznajmy się jeszcze ze zdarzeniami jakie udostępnia nam obiekt klasy XHR i jego właściwościami.
Zdarzenia związane z obiektem klasy XHR
- onRequest - zdarzenie to występuje w momencie wysyłania zapytania,
- onSuccess - zdarzenie, które wystąpi po zakończeniu (odebraniu odpowiedzi) zapytania,
- onStateChange - to zdarzenie występuje w momencie gdy zmieni się stan obiektu XMLHttpRequest,
- onFailure - zdarzenie występujące gdy zaistnieją błędy podczas wykonywania zapytania.
Właściwości obiektu klasy XHR
- running - właściwość będąca wartością logiczną, która określa czy zapytanie jest aktualnie uruchomione (true) czy jest nieaktywne (false),
-
response - jest to właściwość, która zawiera odpowiedź na dane zapytanie (w postaci obiektu). Dostęp do tej właściwości uzyskujemy w momencie wystąpienia zdarzenia onSuccess. Odpowiedź może być zapisana jako tekst lub jako format XML.
Jeśli chcemy uzyskać tekst zapiszemy:
this.response.text;gdy chcemy mieć zwrócony format XML zapiszemy:
this.response.xml;
Metody obiektu klasy XHR
Tworzenie obiektu klasy XHR przebiega w dobrze nam już znany sposób:
var nowy_obiekt = new XHR({opcje});
Wywołanie metod będzie w tym wypadku wyglądało następująco:
nowy_obiekt.metoda(argumenty);
Metody udostępniane przez obiekt XHR to:
- setHeader
- send
- cancel
setHeader
Metoda setHeader służy do dodawania nagłówka do wysyłanego zapytania. Nie zastępuje przy tym nagłówków dodanych za pomocą parametru headers.
Składnia tej metody:
obiekt_XHR.setHeader('nazwa_naglowka','tresc_naglowka');
Na przykład:
obiekt_XHR.setHeader('Content-type','application/x-www-form-urlencoded');
send
Metoda send to to na co chyba wszyscy czekali od początku tej części kursu ;) Służy ona oczywiście do wysyłania zapytań. Po tęgiej dawce teorii czas na praktykę ;) Składnia metody send wygląda następująco:
obiekt_XHR.send('url','dane');
Gdzie argument url to adres na jaki zostanie wysłane zapytanie, a argument dane to ciąg znaków zawierający zmienne zapisane za pomocą znanego nam już z pliku element.form.js zapisu jak uzyskiwaliśmy za pomocą metody toQueryString.
Zacznijmy może od małej "wizualizacji" różnicy pomiędzy zapytaniem synchronicznym i asynchronicznym.
Stwórzmy dwa obiekty klasy XHR, z czego jeden niech będzie wywoływał zapytanie synchroniczne:
var XHR1 = new XHR({async:false});
var XHR2 = new XHR();
Ponieważ warto by podczas rozpoczęcia wywołania pojawił się jakiś "sygnał", że ładowanie jest w toku, definiujemy dla obu obiektów znane nam już zdarzenie onRequest:
var XHR1 = new XHR({
async:false,
onRequest: function(){
$('load').setStyle('display','block');
}
});
var XHR2 = new XHR({
onRequest: function(){
$('load').setStyle('display','block');
}
});
Na koniec warto pokazać jakąś informację, że ładowanie danych się zakończyło i ukryć loader - wykorzystujemy tutaj zdarzenie onSuccess:
var XHR1 = new XHR({
async:false,
onRequest: function(){
$('load').setStyle('display','block');
},
onSuccess: function(){
$('load').setStyle('display','none');
alert('Dane zostały pobrane...');
}
});
var XHR2 = new XHR({
onRequest: function(){
$('load').setStyle('display','block');
},
onSuccess: function(){
$('load').setStyle('display','none');
alert('Dane zostały pobrane...');
}
});
Teraz tylko podpiąć zdarzenia onclick pod odpowiednie buttony i całość pod zdarzenie window.onload, w wyniku czego otrzymamy poniższy przykład:
No a co jeśli zamiast tekstu chcemy pobrać plik XML ? Wiadomo, że XML'a można użyć do eleganckiego obrazowania struktur danych. W kolejnym przykładzie pobierzemy plik XML następującej postaci:
<?xml version="1.0" encoding="UTF-8"?>
<comment>
<author>Jakiś nick</author>
<text>Lorem ipsum dolor sit amet...</text>
<date>2007-06-25</date>
</comment>
Oczywiście nie potrzebujemy wyświetlać na stronie całej treści pliku XML, a tylko interesującą nas zawartość. Jak to zrobić ? Najpierw w ogóle stwórzmy obiekt klasy XHR, który pobierze nam ten plik xml:
var XHR_object = new XHR();
Potrzebujemy wyświetlić jedynie nick, tekst i datę z danego pliku XML w odpowiednich divach zatem korzystamy z metody onSuccess i oczywiście odczytanych danych do których dostęp w postaci pliku XML otrzymamy za pomocą zapisu:
this.response.xml;
Dane z pliku XML odczytamy według tagów - skorzystamy z funkcji getElementsByTagName, a jako nazwę tagu podajemy nazwę interesującej nas pozycji w pliku XML.
Oczywiście trzeba pamiętać, że może istnieć kilka takich samych tagów, a sam zwracany obiekt to kolekcja elementów, zatem należy dodać po funkcji getElementsByTagName zapis item(0) by pobrać pierwszy element. Mamy już dostęp do elementu, ale nas interesuje węzeł tekstowy umieszczony w tym elemencie więc korzystamy z metody firstChild i właściwości data tegoż obiektu.
Finalny wzorzec kodu:
this.response.xml.getElementsByTagName('nazwa_tagu').item(0).firstChild.data;
Teraz tylko scalamy całość w odpowiedni ciąg znaków i można wyświetlić wynik zapytania:
var XHR_object = new XHR({
onSuccess: function(){
var dane = '<strong>Autor komentarza:</strong> ' +
this.response.xml.getElementsByTagName('author').item(0).firstChild.data
+ ' <br /><strong>Treść komentarza:</strong> ' +
this.response.xml.getElementsByTagName('text').item(0).firstChild.data
+ '<br /><strong>Data dodania komentarza:</strong> ' +
this.response.xml.getElementsByTagName('date').item(0).firstChild.data;
$('load').setHTML(dane);
}
});
Wszystko można obejrzeć w akcji w poniższym przykładzie:
Dodajmy teraz do tego kodu obsługę błędu połączenia za pomocą zdarzenia onFailure. Ogranicza się to do dodania dodatkowej opcji i funkcji, która się wykona gdy wystąpi błąd, ja zdecydowałem, że zamiast treści z pliku XML pojawi się komunikat:
var XHR_object = new XHR({
onFailure: function(){
$('load').setHTML('Wystąpił błąd podczas wykonywania zapytania !');
},
onSuccess: function(){
var dane = '<strong>Autor komentarza:</strong> ' +
this.response.xml.getElementsByTagName('author').item(0).firstChild.data
+ ' <br /><strong>Treść komentarza:</strong> ' +
this.response.xml.getElementsByTagName('text').item(0).firstChild.data
+ '<br /><strong>Data dodania komentarza:</strong> ' +
this.response.xml.getElementsByTagName('date').item(0).firstChild.data;
$('load').setHTML(dane);
}
});
Oczywiście przy wysyłaniu zapytania zmieniłem adres pliku na błędny, by zdarzenie mogło zaistnieć ;)
Do obejrzenia w poniższym przykładzie:
Zanim przejdziemy do ostatniej metody klasy XHR chciałbym jeszcze pokazać mały przykład tłumaczący działanie zdarzenia onStateChange. Po pierwsze i najważniejsze - w mootools 1.11 w stosunku do mootools 1.1 ... to zdarzenie przestało działać ;) W sumie nie wiem czy to błąd czy celowe działanie... Fakt faktem informacja o stanie obiektu XMLHttpRequest nie jest może jakaś strasznie ważna, ale mimo to ktoś może potrzebować tej funkcjonalności...
Zacznijmy może od tego jak zmienia się stan obiektu XMLHttpRequest.
Obiekt XMLHttpRequest posiada właściwość readyState, do której używając mootools i klasy XHR mamy dostęp poprzez zapis:
this.transport.readyState;
Wspomniana właściwość może przybierać 5 wartości od 0 do 4:
- 0 - niezainicjowane
- 1 - w trakcie pobierania
- 2 - pobrano
- 3 - interaktywne
- 4 - gotowe
No dobrze - wspominałem, że mootools 1.11 "zgubił" obsługę tego zdarzenia, jak to naprawić ? Potrzebujemy dodać dosłownie 6 krótkich linijek kodu przed użyciem klasy XHR. Cofamy się teraz do czasów omawiania plików z grupy Class i przypominamy sobie metodę extend:
XHR = XHR.extend({
onStateChange: function() {
this.fireEvent('onStateChange', this.transport);
this.parent();
}
});
Po dodaniu tego kodu metoda onStateChange powinna pracować już poprawnie ;] Dodam jeszcze, ze gdybyśmy zastosowali metodę implement (wydawałoby się, że krótszy zapis) to owszem zdarzenie onStateChange zacznie działać, ale zdarzenie onSuccess przestaje występować ;]
Żeby nie być gołosłownym daję poniższy przykład by zaprezentować zdarzenie onStateChange w akcji :
Pora już przejść do ostatniej metody pliku XHR.js - metody cancel
cancel
Metoda cancel służy do przerwania wykonywania danego zapytania obiektu XHR. Jej składnia jest bardzo prosta:
obiekt_XHR.cancel();
Najlepiej będzie zaprezentować jej działanie na przykładzie pierwszym, gdzie wykonywane zapytanie trwa dość długo...
Warto jeszcze wiedzieć, że w momencie anulowania zapytanie, wywoływane jest zdarzenie
onCancel
. Zatem możemy w opcjach obiektu klasy XHR dodać opcję :
onCancel: function(){
// wykonywany kod
}
W poniższym przykładzie prezentuję użycie wyżej wymienionego zdarzenia i samej metody na zmodyfikowanym przykładzie pierwszym:
To by było na tyle, jeśli chodzi o plik XHR.js, w następnej części kursu przejdę do omawiania pliku Ajax.js, który rozszerza możliwości poznanej w tej części kursu klasy XHR.
Komentarze do wpisu "Mootools 1.1 - XHR.js":
1.
daniel napisał(a):
26 października 2008, 09:20:46
Witam, mam następujący problem – wysyłam zapytanie ajaxowe za pomocą XHR, do php i poprawnie otrzymuje dane – kod html z templatki smarty.
Zwykłe teksty np w span itp., wyświetlają się poprawnie, ale wszystkie formularze wraz z całą zawartością są niewidoczne (nie mają żadnego stylu) :/.
Problem występuje w Firefoksie, nie wiem jak w IE.
onSuccess wygląda tak:
<code>$(‘ajax_content’).setHTML;</code>.
Kompletnie nie wiem jak to naprawić, dzięki z góry za pomoc.
Dodaj komentarz: