Ten tutorial jest przeznaczony dla osób pragnących poznać bibliotekę Ext. Prowadzi on krok po kroku przez podstawy działania biblioteki i wyjaśnia jak szybko uruchomić pierwszą dynamiczną stronę. Zakładam, że czytelnik posiada już jakąś wiedzę na temat Java Scriptu oraz obiektowego modelu dokumentu HTML (DOM).

Pobierz Ext

Pierwszą rzeczą, którą należy uczynić jest pobranie biblioteki. Zawsze najnowszą wersję znajdziecie pod adresem http://extjs.com/download. Pod tym adresem znajdziecie kilka opcji do wyboru, ale na początek najlepiej będzie pobrać najświeższą stabilną wersję biblioteki Ext. Po ściągnięciu i rozpakowaniu archiwum najlepiej będzie zacząć oględziny od katalogu examples, gdzie znajdziecie przykładowe skrypty.

Zaczynamy!

Na początek przyjrzymy się najbardziej typowym zadaniom jakie wypełnia się w Java Script’cie i zobaczymy jak sobie z nimi poradzić przy pomocy biblioteki Ext. Na pierwszy ogień proponuję pobrać “zestaw startowy” IntroToExt.zip, którego użyjemy w niniejszym tutorialu. Archiwum to zawiera trzy pliki: ExtStart.html, ExtStart.js oraz ExtStart.css. Wypakuj je do katalogu bezpośrednio pod katalogiem gdzie znajduje się Ext (np. jeśli Ext znajduje się w katalogu “C:\code\Ext\v1.0″, utwórz nowy katalog w “v1.0″ i nazwij go “tutorial”). Kliknij dwa razy ExtStart.htm aby uruchomić go w przeglądarce internetowej i powinieneś zobaczyć komunikat informujący, że skonfigurowałeś wszystko prawidłowo. Jeśli otrzymałeś komunikat o błędzie Java Scriptu, wykonaj instrukcje z tej strony, aby “zmusić” skrypt do poprawnego działania.

Teraz otwórz plik ExtStart.js w swoim ulubionym IDE lub edytorze tekstu i przyjrzyj się następującemu zapisowi:

Ext.onReady(function() {

 alert("Gratulacje! Prawidłowo skonfigurowałeś bibliotekę Ext!");

});

Ext.onReady prawdopodobnie jest pierwszą metodą, z której będziesz korzystał na niemal każdej stronie. Metoda ta jest automatycznie wywoływana w momencie kiedy DOM jest wczytany w całości, co gwarantuje, że dowolny element strony będzie dostępny dla skryptu. Teraz wykasuj linijkę zawierającą alert() - zaczynamy dodawać prawdziwy kod, który będzie robił coś pożytecznego!

Element: serce Ext

Praktycznie rzecz biorąc, prawie wszystkie zadania w Java Script’cie bazują na odwołaniach do konkretnych elementów strony aby można było na nich wykonać jakiekolwiek operacje. W tradycyjnym Java Script’cie, aby dostać się do określonego węzła DOM, należy wykonać następujący kod:

var myDiv = document.getElementById('myDiv');

To oczywiście działa, ale zwrócony obiekt (węzeł DOM) niestety oferuje nam zbyt wiele. Aby zrobić z nim cokolwiek trzeba napisać się mnóstwo kodu. W dodatku trzeba pamiętać o kompatybilności kodu z różnymi przeglądarkami co na dłuższą metę może wpędzić człowieka do grobu. Użyjmy więc obiektu Ext.Element. Element jest sercem Ext - większość rzeczy które robisz wymaga odwoływania się do elementów strony i wykonywania na nich przeróżnych akcji. Dlatego API Element-u jest podstawą biblioteki Ext i jeśli masz poświęcić czas na nauczenie się tylko jednej klasy Ext, to właśnie Element jest tą klasą.

Aby otrzymać Ext Element na podstawie jego ID należy wykonać poniższy kod (w stronie ExtStart.htm znajduje się div mający id “myDiv,” możemy więc dodać poniższy kod do pliku ExtStart.js):

Ext.onReady(function() {

 var myDiv = Ext.get('myDiv');

});

A więc otrzymujemy obiekt Element - i co w nim takiego ciekawego?

  • Element opakowuje większość metod i właściwości DOM, które będą Ci potrzebne, zapewniając przy tym wygodny, zunifikowany i wspólny dla wszystkich przeglądarek interfejs (a poza tym w dalszym ciągu masz bezpośredni dostęp do węzła DOM poprzez użycie Element.dom)
  • metoda Element.get() zapewnia wewnętrzne cache’owanie, więc kolejne wywołania tego samego obiektu są bardzo szybkie
  • najczęstsze akcje wykonywane na węzłach DOM są wbudowane w bezpośrednie, wspólne dla wszystkich przeglądarek metody obiektu Element (dodaj/usuń klasy CSS, dodaj/usuń handlery zdarzeń, położenie, rozmiar, animacja, drag/drop, itp.)

Wszystko to oznacza, że możesz wykonać bardzo dużo przy użyciu minimum kodu. Poniżej znajduje się kilka przykładów. Zajrzyj do dokumentacji Element API, aby zobaczyć wszystko, co możesz zrobić przy użyciu obiektu Element. Dodaj poniższy kod do pliku ExtStart.js zaraz poniżej linijki, w której pobieraliśmy uchwyt do Elementu ‘myDiv’:

myDiv.highlight();      // tło elementu rozbłyśnie na żółto a następnie wróci do oryginalnego koloru

myDiv.addClass('red');  // doda klasę CSS (zdefiniowaną w pliku ExtStart.css)

myDiv.center();         // wycentruje element w oknie przeglądarki

myDiv.setOpacity(.25);  // uczyni element częściowo przezroczystym

Wybieranie węzłów DOM

Dosyć często nie da się (lub jest to po prostu nie wygodne) wybrać elementu DOM po ID. Może byćtak, że ID nie jest ustawione lub po prostu nie znamy jego wartości lub chcemy dostać się do wielu elementów na raz. Czasem zachodzi więc potrzeba wybierania węzłów na podstawie czegoś innego niż ID, np. inny atrybut albo klasa CSS. Z tych właśnie powodów Ext został wyposażony w niezwykle użyteczną bibliotekę DomQuery.

DomQuery może być wykorzystywana jako samodzielna biblioteka, jednakże zazwyczaj podczas korzystania z Ext, będziemy z niej korzystać w kontekscie wybierania Elementów, dzięki czemu będzie można następnie działać na nich poprzez interfejs klasy Element. Na szczęście, obiekt Element sam z siebie wspiera wybieranie poprzez metodę Element.select, która wewnętrznie wykorzystuje DomQuery do wybierania elementów. Jako prosty przykład wykorzystania tej metody, plik ExtStart.html zawiera kilka paragrafów (<p>), z których żaden nie ma przypisanego ID. Jeśli byśmy chcieli wybrać wszystkie paragrafy i wykonać na nich jakąś akcję wystarczy wykonać kod podobny do poniższego:

// Podświetla każdy paragraf

Ext.select('p').highlight();

Powyższy przykład pokazuje bardzo użyteczny aspekt metody Element.select - metoda ta zwraca nam CompositeElement, który umożliwia nam dostęp do każdego Elementu leżącego poniżej poprzez interfejs klasy Element. Pozwala nam to działać na każdej instancji Elementu zwróconej przez Element.select bez potrzeby odwoływania się do każdej z nich w pętli.

DomQuery wspiera szeroki wachlarz opcji wybierania, w tym większość selektorów W3C CSS3 DOM, podstawowe XPath, atrybuty i wiele innych. Szczegóły znajdziecie w dokumentacji API DomQuery.

Reagowanie na zdarzenia

Do tej pory w naszych przykładach wpisywaliśmy kod wewnątrz funkcji onReady, co powodowało, że nasz kod był uruchamiany zaraz po załadowaniu strony. Niestety, ten sposób nie daje nam zbyt wiele kontroli - a przecież w większości przypadków chcemy uruchamiać odpowiedni kod w odpowiedzi na konkretne akcje lub zdarzenia, które chcielibyśmy obsłużyć. Aby tego dokonać musimy zdefiniować uchwyty do obsługi zdarzeń, które będą odpowiadać na konkretne zdarzenia przy pomocy funkcji, które im przypiszemy.

Przejdźmy więc do przykładu. Otwórz ExtStart.js i zmień jego zawartość tak, aby wyglądał jak poniżej:

Ext.onReady(function() {

 Ext.get('myButton').on('click', function(){

 	alert("Kliknąłeś guzik!");

 });

});

Ten kod w dalszym ciągu jest uruchamiany dopiero po załadowaniu strony, ale jest tu jedna bardzo istotna różnica. Funkcja zawierająca alert() jest zdefiniowana, ale nie jest od razu uruchamiana - jest przypisana do uchwytu obsługującego zdarzenie polegające na kliknięciu guzika. Mówiąc po polsku: “złap Element mający ID ‘myButton’ i przypisz mu funkcję, która będzie wywoływana za każdym razem, gdy ktoś kliknie ten Element”.

Oczywiście, Element.select pozwala wykonać to samo, ale z całą grupą Elementów na raz. Na przykład, aby wyświetlić komunikat, gdy klikniemy dowolny paragraf, wystarczy wykonać poniższy kod:

Ext.onReady(function() {

 Ext.select('p').on('click', function() {

 	alert("Kliknąłeś paragraf");

 });

});

W powyższych dwóch przykładach funkcja obsługująca zdarzenie zdefiniowana jest od razu, bez nadawania funkcji nazwy. Naywa się to “funkcją anonimową”. Ale można też przypisać zdarzenie tak, aby było obsługiwane przez funkcję nazwaną, co jest szczególnie przydatne jeśli taka sama funkcja ma obslugiwać wiele zdarzeń. Na przykład, poniższy kod jest funkcjonalnie tożsamy z poprzednim przykładem.

Ext.onReady(function() {

 var paragraphClicked = function() {

 	alert("Kliknąłeś paragraf");

 }

 Ext.select('p').on('click', paragraphClicked);

});

Do tej pory wykonywaliśmy prostą akcję w momencie kiedy dochodziło do określonego zdarzenia, ale warto by było wiedzieć, który konkretnie Element wywołał zdarzenie aby można było przeprowadzić akcję właśnie na tym elemencie. To też jest bardzo proste - metoda Element.on przekazuje do funkcji obsługującej zdarzenie trzy bardzo przydatne parametry (teraz przyjrzymy się tylko jednemu, ale w dokumentacji API znajdziesz więcej informacji na ten temat). W naszych poprzednich przykładach funkcja obsługująca zdarzenie ignorowała te parametry, ale dokonując niewielkiej modyfikacji kodu dodamy mu nową funkcjonalność. Pierwszym i najważniejszym parametrem jest zdarzenie. Jest to obiekt zdarzenia Ext, który jest zormalizowany dla wszystkich przeglądarek i zapewnia więcej informacji niż standardowe zdarzenie. Na przykład, aby dostać się do docelowego węzła DOM zdarzenia należy wykonać poniższy kod:

Ext.onReady(function() {

 var paragraphClicked = function(e) {

 	Ext.get(e.target).highlight();

 }

 Ext.select('p').on('click', paragraphClicked);

});

Zauważ, że celem jest węzeł DOM, więc najpierw musimy dostać się do odpowiadającego mu Elementu (za pomocą Ext.get), a następnie możemy przeprowadzić na nim akcję (highlight() ). W naszym przypadku podświetlaliśmy paragraf.

Używanie widgetów

Poza omówioną powyżej funkcjonalnością, Ext zawiera jeden z najbogatszych zestawów Java Scriptowych widgetów do tworzenia bogatych interfejsów użytkownika. Jest ich zbyt wiele by omówić je wszystkie tutaj, ale przyjrzyjmy się kilku najczęściej używanym.

MessageBox

Pójdźmy krok dalej, poza wyświetlanie nudnego komunikatu “Hello World”. Mamy już kod, napisany w poprzedniej sekcji, który podświetla każdy kliknięty paragraf. Zmodyfikujmy go więc tak, aby dodatkowo treść klikniętego paragrafu została wyświetlona w okienku komunikatu. W funkcji paragraphClicked zamieńmy tą linijkę kodu:

Ext.get(e.target).highlight();

na ten kod:

var paragraph = Ext.get(e.target);

 paragraph.highlight();	Ext.MessageBox.show({

 	title: 'Kliknięty paragraf',

 	msg: paragraph.dom.innerHTML,

 	width:400,

 	buttons: Ext.MessageBox.OK,

 	animEl: paragraph

 });

Jest tu kilka nowych rzeczy, które warto pokrótce omówić. W pierwszym wierszu tworzymy lokalną zmienną o nazwie paragraph, która będzie przechowywać odwołanie do Elementu reprezentującego kliknięty węzeł DOM (w naszym przypadku wiemy, że zawsze będzie to paragraf). Dlaczego to robimy? Spójrzmy nieco dalej w kod. Potrzebujemy odwołania do Elementu, który będziemy podświetlać, ale także będziemy korzystać z tego samego Elementu, aby pobrać określone parametry dla MessageBox-a. Ogólnie rzecz biorąc, nie jest dobrą praktyką wywoływać tą samą funkcję, tylko po to, aby pobrać te same wartości czy odwołania do obiektów. Dlatego należy przypisać wynik działania funkcji do zmiennej lokalnej i dalej używać tej zmiennej.

Przejdźmy teraz do wywołania MessageBoxa, które przy okazji pokazuje nam nowe zagadnienia do omówienia. Na pierwszy rzut oka, wygląda to na zwykłą listę parametrów przekazywanych do metody, ale jeśli przyjrzymy się nieco bardziej to zwróci naszą uwagę dość specyficzna składnia. To co jest przekazywane do MessageBox.show() w naszym przypadku jest tak naprawdę tylko jednym parametrem: obiekt-literał, który zawiera zestaw właściwości i wartości. W Javascript’cie, obiekt-literał jest dynamicznym, podstawowym obiektem, który jest tworzony za każdym razem kiedy używasz znaków “{” oraz “}” (nawiasów klamrowych) otaczających listę nazw/wartości właściwości, zaś format tych właściwości ma postać [nazwa właściwości] : [wartość właściwości]. Wzorzec ten jest szeroko wykorzystywany w Ext, warto więc przyswoić go sobie!

Dlaczego warto używać obiektu-literału? Głównym powodem jest elastyczność. Nowe właściwości mogą być dodawane lub usuwane w dowolnym momencie, mogą też być definiowane w dowolnej kolejności, podczas gdy definicja metody (liczba i rodzaj parametrów oczekiwanych przez metodę) pozostaje bez zmian. Ponadto, używanie metod z wieloma opcjonalnymi parametrami (jak w przypadku MessageBox.show) jest o wiele wygodniejsze dla programisty. Na przykład, weźmy sobie fikcyjną metodę foo.action mającą cztery opcjonalne parametry, ale chcemy przekazać tylko jeden z nich. W takim przypadku kod wyglądałby tak:

foo.action(null, null, null, 'hello')

Jednakże kiedy użyjemy obiektu-literału, kod będzie wyglądał tak:

foo.action({ param4: 'hello' })

Łatwiejszy w użyciu i bardziej czytelny.

Grid

Grid to jeden z najbardziej popularnych widgetów w Ext i zazwyczaj pierwszy, który ludzie chcą zobaczyć. Zobaczmy więc jak łatwo można zbudować podstawową wersję grida. Zamień zawartość pliku ExtStart.js na poniższy kod:

Ext.onReady(function() {

 var myData = [

 	['Apple',29.89,0.24,0.81,'9/1 12:00am'],

 	['Ext',83.81,0.28,0.34,'9/12 12:00am'],

 	['Google',71.72,0.02,0.03,'10/1 12:00am'],

 	['Microsoft',52.55,0.01,0.02,'7/4 12:00am'],

 	['Yahoo!',29.01,0.42,1.47,'5/22 12:00am']

 ];	var ds = new Ext.data.Store({

 	proxy: new Ext.data.MemoryProxy(myData),

 	reader: new Ext.data.ArrayReader({id: 0}, [

 		{name: 'firma'},

 		{name: 'cena', type: 'float'},

 		{name: 'zmiana', type: 'float'},

 		{name: 'procentZmiana', type: 'float'},

 		{name: 'ostatniaZmiana', type: 'date', dateFormat: 'n/j h:ia'}

 	])

 });

 ds.load();

var colModel = new Ext.grid.ColumnModel([

 	{header: "Firma", width: 120, sortable: true, dataIndex: 'firma'},

 	{header: "Cena", width: 90, sortable: true, dataIndex: 'cena'},

 	{header: "Zmiana", width: 90, sortable: true, dataIndex: 'zmiana'},

 	{header: "Zmiana w %", width: 90, sortable: true, dataIndex: 'procentZmiana'},

 	{header: "Last Updated", width: 120, sortable: true,

 		renderer: Ext.util.Format.dateRenderer('m/d/Y'),

                        dataIndex: 'ostatniaZmiana'}

 ]);

var grid = new Ext.grid.Grid('grid-example', {ds: ds, cm: colModel});

 grid.render();

 grid.getSelectionModel().selectFirstRow();

});

Wygląda na dużo więcej, ale tak naprawdę to tylko siedem linijek kodu. Pierwszy wiersz tworzy tablicę danych, które będą wyświetlane w naszym gridzie. W normalnych warunkach będziemy zapewne wczytywać dane z jakiegoś dynamicznego źródła, np. z bazy danych lub web service’u. Następnie tworzymy i wczytujemy magazyn danych (ang. data store), który “wytłumaczy” bibliotece Ext w jaki sposób ma wczytać i sformatować dane. Następnie definiujemy model kolumn (ang. column model), który pozwala nam skonfigurować każdą kolumnę naszego grida. W końcu, tworzymy widget grida i przekazujemy mu magazyn danych i model kolumn, renderujemy go i zaznaczamy pierwszy wiersz. Proste, prawda? Jeśli wszystko poszło dobrze, powinieneś w przeglądarce otrzymać coś podobnego do tego co znajduje się na poniższym obrazku.

Przykładowy Ext grid

Oczywiście, w powyższym kodzie znajduje się kilka elementów, których nie omówiliśmy a które zapewne nie są dla czytelnika jasne (np. MemoryProxy), ale na tym etapie chodzi o to, żeby przekonać się, jak przy użyciu zaledwie kilku lini kodu można stworzyć bogaty, złożony i atrakcyjny wizualnie komponent interfejsu użytkownika. Więcej na temat grida znajdziesz w dokumentacji - zarówno opis metod, jak i przykłady.

I dużo więcej…

To co zostało tu zaprezentowane to jedynie wierzchołek góry lodowej. Ext jest wyposażony w wiele przeróżnych widgetów, takich jak np. automatyczne układy stron, zakładki, menu, belki narzędziowe, okna dialogowe, drzewka i wiele innych. Aby dowiedzieć się na ten temat więcej, polecam lekturę dokumentacji.

Używanie Ajax-a

Kiedy mamy już stronę, z którą można oddziaływać za pomocą JavaScriptu, warto by było dowiedzieć się jak pobierać i wysyłać dane do i z serwera (np. pobieranie i zapisywanie danych w bazie). Możemy to zrobić asynchronicznie, bez przeładowywania całej strony, czyli mówiąc wprost - użyć Ajaxa. Ext ma oczywiście wbudowane wsparcie dla tej technologii. Na przykład możemy w odpowiedzi na działanie użytkownika przesłać do serwera jakąś informację a następnie zaktualizować element interfejsu użytkownika jako wynik jego działania. Poniżej znajduje się bardzo prosty HTML zawierający pole tekstowe, guzik oraz div-a, który będzie użyty do wyświetlenia komunikatu. Uwaga: możesz wpisać ten kod do pliku ExtStart.html, ale musisz mieć dostęp do serwera WWW aby móc go uruchomić.

<div id="msg" style="visibility: hidden"></div>

Name: <input type="text" id="name" />

<input type="button" id="okButton" value="OK" />

Następnie dodamy nieco JavaScriptu potrzebnego nam do pobrania i wysłania danych z i do serwera. Zamień kod w pliku ExtStart.js na poniższy:

Ext.onReady(function(){

 Ext.get('okButton').on('click', function(){

 	var msg = Ext.get('msg');

 	msg.load({

 		url: dane.php, // < -- zastąp swoim adresem url

 		params: 'name=' + Ext.get('name').dom.value,

 		text: 'Aktualizowanie...'

 	});

 	msg.show();

 });

});

Ten kod powinien już być dla nas zrozumiały. Kod opakowuje okButton przy pomocy obiektu Element i dołącza do niego anonimową funkcję, która przetworzy zdarzenie kliknięcia guzika. Wewnątrz kodu obsługującego zdarzenie kliknięcia guzika używamy specjalnej klasy wbudowanej w Ext o nazwie UpdateManager - użycie tej klasy powoduje, że wysłanie żądania przy pomocy Ajaxa, odebranie odpowiedzi i zaktualizowanie innego Elementu na stronie staje się bardzo proste. UpdateManager może być użyty bezpośrednio, lub tak jak my tu robimy, można się do niego odwołać za pośrednictwem Elementu, który chcemy zaktualizować (w naszym wypadku div ‘msg’) używając w tym celu metody Element.load. Gdy używamy Element.load odpowiedź serwera automatycznie podmienia innerHTML Elementu. Wystarczy przekazać adres url do procesu po stronie serwera, który przetworzy żądanie, łańcuch zawierający parametry żądania (w naszym przypadku zawartość pola ‘name’) oraz tekst, który będzie wyświetlany podczas przetwarzania żądania. Na koniec tylko trzeba tylko pokazać div-a msg (początkowo ustawiliśmy go jako hidden) i to wszystko! Oczywiście, jak większość rzeczy w Ext, UpdateManager posiada o wiele więcej opcji, z których można korzystać. Ext umożliwia także różne sposoby przetwarzania żądań Ajaxa w zależności od sytuacji. Tutaj chodziło nam o pokazanie najprostszego przykładu.

Ostatnim kawałkiem tej Ajaxowej układanki jest przetworzenie żądania po stronie serwera i odesłanie wyniku do strony. Po stronie serwera odbiorcą danych może być cokolwiek: strona działająca po stronie serwera, servlet, web service a nawet skrypt Perl czy CGI - cokolwiek stojącego po stronie serwera zdolnego przetworzyć żądanie HTTP. Poniżej prezentuję kilka przykładów w różnych językach (zadaniem poniższego kodu jest pobranie treści, która był wpisana do formularza w pole ‘name’ i odesłanie do strony tego samego, ale poprzedzonego słowami ‘Od serwera: ‘ - taka odpowiedź zostanie wpisana wewnątrz div-a ‘msg’):

PHP

<?php

if(isset($_POST['name']))

 {

 	echo 'Od serwera: '.$_POST['name'];

 }

?>

ASP.NET

protected void Page_Load(object sender, EventArgs e)

{

 if (Request.Form["name"] != null)

 {

 	Response.Write("Od serwera: " + Request.Form["name"]);

 	Response.End();

 }

}

Cold Fusion

<cfif StructKeyExists(form, "name")>

 <cfoutput>Od serwera: #form.name#</cfoutput>

</cfif>

Prawdziwym wyzwaniem przy pracy z Ajaxem jest jednak napisanie odpowiedniego kodu, który prawidłowo przetworzy i sformatuje dane po stronie serwera. Mamy kilka formatów do wyboru, najczęściej jest to JSON lub XML. Dostępne są różne biblioteki dla różnych języków programowania, które ułatwiają życie przy przetwazraniu danych przy użyciu Ajaxa, a z których można korzystać przy pracy z Ext ponieważ Ext jest neutralny jeśli chodzi o język stosowany po stronie serwera. Ext nie wnika w to, co się dzieje po stronie serwera, przynajmniej tak długo, jak dane przesłane do strony są poprawnie sformatowane.

Co dalej?

Teraz, gdy posmakowałeś czym jest Ext i co oferuje, polecam zapoznać się z wieloma dostępnymi materiałami, które pozwolą zagłębić się na kolejne poziomy zaawansowania:

  • dokumentacja API Ext JS dla programisty
  • forum użytkowników Ext JS

Tłumaczenie przygotowałem na podstawie angielskiej wersji autorstwa Briana Moeskau oraz rosyjskiej autorstwa Mikhaela Y. Korneeva znajdujących się w zasobach Ext Wiki