2007.06_AJAX w CakePHP _[Ajax].pdf
(
545 KB
)
Pobierz
440328207 UNPDF
Rozwiązania
AJAX w CakePHP
Zwiększenie funkcjonalności serwisu
Tradycyjne serwisy WWW przeważnie posiadają interfejs użytkownika
zbudowany z wykorzystaniem formularzy HTML. Powoduje to, iż nie są
interaktywne – wprowadzane dane są wysyłane do serwera, co z kolei
powoduje odświeżanie całej strony.
Dowiesz się...
• Poznasz praktyczne zastosowanie technologii
AJAX w środowisku Framework'u CakePHP. Po
analizie przedstawionego kodu będziesz mógł
wzgobacić własny serwis o dynamicznie pre-
zentowane dane.
Powinieneś wiedzieć...
• Wymagana jest znajomość Framework'u Cake-
PHP, umożliwiająca samodzielne skoniguro-
wanie środowiska Apache, MySQL i PHP. Przy-
datna będzie także znajomość podstaw języ-
ka JavaScript.
Script, to o wiele wygodniej jest operować
na danych, które w prosty sposób można
przetwarzać w tym języku;
•
Cascading StyleSheets
(
CSS
) – kaskadowe
arkusze stylów to język służący do opisu
sposobu wizualizacji stron WWW. Za je-
go pomocą można przypisać atrybuty, takie
jak np.: kolor, wielkość czcionki, margine-
sy do poszczególnych elementów XHTML
lub XML;
•
Document Object Model
(
DOM
) – obiektowy
model umożliwiający nawigację w struktu-
rze dokumentu XML oraz modyfikację za-
wartości jego określonych fragmentów;
•
JSON
(ang.
JavaScript Object Notation
)
umożliwia prostą zamianę obiektów Java-
Script do postaci tekstu (tzw. proces seria-
lizacji), który może być przesyłany za po-
mocą XHR. Proces odwrotny przebiega
równie sprawnie.
Poziom trudności
iwem łączącym DOM z XHR (opis poni-
żej). Innymi słowy, pozwala na aktualiza-
cję fragmentów stron WWW o dane ode-
brane dynamicznie za pomocą XHR;
•
XMLHttpRequest
(
XHR
) – obiekt umoż-
liwiający wymianę danych między serwe-
rem WWW i przeglądarką. Pomimo XML
w nazwie, XHR nie wymusza używania
tego formatu przy przesyłaniu informacji.
Ponieważ, po otrzymaniu danych, ich dal-
sza obróbka odbywa się za pomocą Java-
fika, szablony stylów, informacje pobiera-
ne ze źródeł danych – mimo, że strona po-
zostaje prawie niezmieniona! Technologia AJAX
(ang. Asynchronous JavaScript And XML)
powsta-
ła, by zerwać z dotychczasowym modelem funk-
cjonowania aplikacji webowych. Jest świetnym na-
rzędziem do tworzenia interaktywnych witryn
internetowych, które umożliwia maksymalnie
upodobnić aplikacje webowe do ich odpowied-
ników ze stacji roboczych. Wprowadza asynchro-
niczną komunikację między przeglądarką użyt-
kownika i serwerem WWW, która pozwala na ak-
tualizację tylko wymaganych fragmentów stron,
zamiast przeładowywania całej zawartości.
W artykule przedstawimy praktyczne wpro-
wadzenie do technologii AJAX, na przykładzie
framework'u CakePHP. Zbudujemy przykłado-
wy serwis WWW ilustrujący korzyści (i ograni-
czenia) AJAX.
Czym jest AJAX?
AJAX nie jest językiem programowania stron
WWW. To nowy sposób wykorzystania zna-
nych wcześniej technologii:
•
JavaScript
– język programowania działa-
jący w środowisku przeglądarki interne-
towej, po stronie użytkownika. Jest spo-
Rysunek 1.
Zasilanie aplikacji danymi – tryb scafold
38
06/2007
P
rzeładowywana jest cała zawartość – gra-
AJAX
Na szczęście nie ma potrzeby, by samodziel-
nie tworzyć kod, umożliwiający połączenie
powyższych technologii. Nie ma również po-
trzeby samodzielnego operowania na pozio-
mie obiektu XHR, czy troszczenia się o współ-
pracę z różnymi typami przeglądarek inter-
netowych. W internecie znajduje się wiele go-
towych bibliotek rozszerzających JavaScript
i ułatwiających integrację z PHP. W dalszej
części artykułu skupimy się na bibliotekach
Prototype oraz script.aculo.us, których obsłu-
ga jest częścią funkcjonalności framework'u
CakePHP.
nie ma konieczności przechodzenia mię-
dzy stronami, więc pojęcie
poprzednia stro-
na
traci swoje znaczenie).
o elementy zwiększające jej interaktywność
(i atrakcyjność). Model danych, który wyko-
rzystamy będzie prosty: pojedyncza tabela po-
wiązana z modelem
Task
(pol.
zadanie
). Szcze-
góły związane z implementacją tej części apli-
kacji znajdują się na Listingach 1. oraz 2.
Przygotowane struktury nie zawierają jesz-
cze danych. Uzupełnimy je, wykorzystując
tryb
scaffold
, w którym CakePHP automaty-
cznie stworzy interfejs, umożliwiający dostęp
do danych za pomocą strony WWW. Tryb ten
jest aktywowany, gdy w kontrolerzejest zdefi-
niowana zmienna
$scaffold
. Nie ma potrzeby
definiowania funkcji obsługi modelu danych
– framework zrobi to za nas automatycznie.
Utworzymy teraz kontroler TasksControl-
ler odpowiadający modelowi danych
Task
(Listing 3.).
CakePHP
Jak już wiemy, obsługa AJAX w CakePHP,
wymaga obecności biblioteki
Prototype
oraz
script.aculo.us
. Niestety nie są one częścią
framework'u – musimy je samodzielnie po-
brać z internetu oraz zmodyfikować konfigura-
cję CakePHP, by ich obecność została uwzględ-
niona przy tworzeniu dynamicznych stron
WWW.
Zacznijmy od przygotowania prostego ser-
wisu, umożliwiającego zapisywanie do bazy
danych informacji wprowadzanych do formu-
larza. Najpierw przygotujemy wersję działają-
cą bez AJAX, którą następnie rozbudujemy
Korzyści ze stosowania
technologii AJAX
• AJAX umożliwia wykorzystanie doświad-
czeń użytkowników pracujących z aplika-
cjami desktopowymi. Podobne zachowanie
aplikacji, zbudowanej z wykorzystaniem
AJAX, może sprawić, iż będą one używa-
ne efektywniej;
• AJAX pozwala na ograniczenie ilości da-
nych przesyłanych między przeglądar-
ką internetową użytkownika i serwerem
WWW, przy zachowaniu dotychczaso-
wych funkcjonalności. Zamiast przesy-
łania zawartości całych stron, aktualizo-
wane są tylko te fragmenty, które uległy
zmianie;
• AJAX umożliwia tworzenie interaktyw-
nych metod prezentacji danych, które nie
były możliwe przy zastosowaniu dotych-
czasowych technologii (np.
drag'n'drop
, dy-
namicznie rozwijające się listy czy auto-
matyczne uzupełnianie tekstów wprowa-
dzanych do formularzy).
Ograniczenia
Rysunek 2.
Wynik działania przykładowej aplikacja działającej bez AJAX
• AJAX wymaga od użytkowników, by po-
sługiwali się nowoczesnymi przeglądar-
kami obsługującymi technologie: DOM,
JavaScript, CSS. Wymaga to również posia-
dania równie nowoczesnych komputerów
o odpowiedniej mocy obliczeniowej;
• AJAX to dodatkowe kilobajty kodu, któ-
ry musi być dołączony do strony WWW.
W przypadku bibliotek
Prototype
oraz
script.aculo.us
należy się liczyć z dodat-
kowymi 200KB kodu przesyłanych do
przeglądarki użytkownika. Ilość nowo
wprowadzanych danych można w znacz-
nym stopniu zmniejszyć, wcześniej kom-
presując przesyłane pliki (zobacz „kom-
presowanie komponentów”);
• AJAX zmienia funkcjonalność serwisów
WWW, ale nie przeglądarki internetowej.
Funkcje związane z przeglądaniem histo-
rii stron, w tym i przycisk
wstecz
, przywo-
łujący oglądane wcześniej dane, będą za-
chowywały się odmiennie, co może wpro-
wadzać użytkowników w błąd (w AJAX
Rysunek 3.
Przykładowa aplikacja wykorzystująca technologię AJAX
www.phpsolmag.org
39
Rozwiązania
Listing 1.
Struktura danych wykorzystywana w przykładowej aplikacji
CREATE TABLE `tasks`
(
`id`
int
(
10
)
NOT NULL auto_increment,
`title` varchar
(
60
)
,
`done`
int
(
1
)
NOT NULL
default
'0'
,
PRIMARY KEY
(
`id`
)
)
ENGINE=MyISAM;
Nasza przykładowa aplikacja jest już goto-
wa. Wystarczy uruchomić przeglądarkę in-
ternetową i wpisać adres IP serwera Cake-
PHP, razem z nazwą kontrolera np.
http://
127.0.0.1/tasks
. Teraz możemy w prosty
sposób wprowadzić dane, które później bę-
dziemy wyszukiwać za pomocą formularza
HTML (Rysunek 1.).
Pobieranie danych tak zdefiniowanych struk-
tur powierzymy kontrolerowi TodoControl-
ler (Listing 3.) oraz domyślnemu widoko-
wi
(plik app/views/todo/index.thtml)
,
w którym umieścimy kod generujący formu-
larz WWW (Listing 4.).
Po zasileniu testowymi danymi, nasza
aplikacja zachowuje się w sposób standar-
dowy – wprowadzenie informacji do for-
mularza i naciśnięcie przycisku
Wyślij
, po-
woduje przeładowanie całej strony i wyświe-
tlenie tytułów zadań pasujących do zadane-
go kryterium (Rysunek 2.). Wybierane są za-
dania nie ukończone (Task.done = 0) o na-
zwie identycznej z podanym ciągiem zna-
ków
(Task.title = $title)
, posortowa-
ne według kolejności utworzenia
(Task.id
DESC).
Listing 2.
Model danych Task
// plik app/models/task.php
class
Task
extends
AppModel
{
// jeżeli używamy PHP4 musimy zdeiniować zmienną zawierającą nazwę klasy
var
$name
=
'Task'
;
// domyślnie nazwa klasy odpowiada nazwie tabeli w liczbie pojedynczej
// jeżeli jest inaczej i np. zamierzamy wykorzystać
// inną tabelę z bazy danych musimy zdeiniować jej nazwę
var
$useTable
=
'tasks'
;
}
Listing 3.
Kontroler odpowiadający za zasilanie struktur danych informacjami z formularza WWW
(tryb scafold)
// plik app/controllers/tasks_controller.php
class
TasksController
extends
AppController
{
Wprowadzamy
technologię AJAX
Zastanówmy się teraz jak, w naszym uprosz-
czonym przykładzie, wykorzystać technologię
AJAX. Wiemy już, że AJAX umożliwia dyna-
miczną (przebiegającą w tle) wymianę infor-
macji między serwerem WWW i przeglądar-
ką internetową. Możemy więc spróbować prze-
szukiwać bazę danych, w miarę jak użytkownik
wprowadza dane do formularza. Za każdym ra-
zem, gdy zmieni się kryterium wyszukiwania,
przeglądarka automatycznie wyśle zapytanie
do naszej aplikacji, która sprawdzi, czy odpo-
wiednie rekordy znajdują się w bazie danych.
Przekazana z serwera odpowiedź zostanie wy-
świetlona, dając wizualną podpowiedź o rezul-
tatach poszukiwań.
Realizacja powyższego schematu funkcjono-
wania aplikacji wymaga od nas kilku dodatko-
wych zabiegów:
// jeżeli używamy PHP4 musimy zdeiniować zmienną zawierającą nazwę klasy
var
$name
=
'Tasks'
;
var
$uses
=
array
(
'Task'
)
;
var
$helpers
=
array
(
'Html'
)
;
var
$scaffold
;
}
// plik app/controllers/todo_controller.php
class
TodoController
extends
AppController
{
// jeżeli używamy PHP4 musimy zdeiniować zmienną zawierającą nazwę klasy
var
$name
=
'Todo'
;
var
$uses
=
array
(
'Task'
)
;
var
$helpers
=
array
(
'Html'
,
'Time'
)
;
// nie wyświetlaj żadnych informacji
function index(
)
{
}
function search(
)
{
if
(
!
empty
(
$this
–
>
data
))
{
$data
=
$this
–
>
data;
$title
= strip_tags
(
$data
[
'Task'
][
'title'
])
;
$tasks
=
$this
–
>
Task–
>
indAll
(
array
(
'Task.done'
=
>
0,
'Task.title'
=
>
$title
)
, null,
'Task.id DESC'
)
;
$this
–
>
set
(
'todo'
,
$tasks
)
;
}
else
{
$this
–
>
redirect
(
'/todo/index'
)
;
}
• musimy cyklicznie badać, czy w formula-
rzu pojawiły się nowe dane;
• musimy utworzyć metodę kontrolera, któ-
ra będzie sprawdzała obecność wyszuki-
wanych informacji w bazie danych;
• musimy znaleźć sposób, by informacje zwra-
cane przez serwer pojawiały się w prze-
glądarce bez konieczności przeładowania
całej strony WWW.
Na szczęście nie jesteśmy pierwszymi, któ-
rzy zadają sobie pytanie Jak to zrobić? Wspo-
mniana wcześniej biblioteka Prototype zawie-
ra wszystkie niezbędne funkcje, które maksy-
malnie ułatwią nam to zadanie. Uwolni ona
nas od konieczności samodzielnego badania
}
}
40
06/2007
AJAX
Listing 4.
Widoki deiniujące formularz WWW
stanu formularza, obsługi obiektu XHR i ak-
tualizacji strony WWW o zwrócone informa-
cje. Po naszej stronie pozostanie tylko przygo-
towanie metody kontrolera, odpowiedzialne-
go za wyszukiwanie danych (będzie wywo-
ływany przy każdej aktualizacji formularza)
oraz powiązanie ze sobą odpowiednich kom-
ponentów CakePHP i Prototype.
Zacznijmy od początku. Włączenie obsługi
Prototype i script.aculo.us w CakePHP
wymaga
pobrania bibliotek i umieszczenia ich w kata-
logu
app/webroot/js
. W kolejnym kroku mu-
simy zmodyfikować domyślny szablon serwi-
su: wymusić kodowanie UTF–8 (niezbędne do
poprawnej pracy AJAX) oraz załadować biblio-
teki Prototype oraz
script.aculo.us
(Listing
5.). Następnie dołączamy do kontrolera dodat-
kowe moduły, przez umieszczenie deklaracji:
// plik app/views/todo/index.thtml
<
?php
echo $html–
>
formTag('/todo/search');
?>
<
div
>
Wyszukiwanie zadań:
<
div
>
<
?php
echo $html–
>
input('Task/title', array('id' => 'search'));
?>
<
?php
echo $html–
>
submit('Wyślij');
?>
<
/div
>
<
div
>
<
/div
>
<
/form
>
// plik app/views/todo/search.thtml
var $helpers = array('Html',
'Javascript', 'Ajax');
<
h2
>
Znalezione zadania:
<
/h2
>
<
?php if (count($todo)
>
0): ?>
<
ul
>
<
?php foreach ($todo as $task):
?
>
<
li
>
<
?php
echo $task[
'Task'
][
'title'
];
?
>
<
/li
>
<
?php endforeach
?
>
<
/ul
>
<
?php else:
?
>
<
p
>
Nic nie znaleziono!?
<
/p
>
<
?php endif
?
>
<
?php
echo $html–
>
link('> powrót do formularza wyszukiwania', '/todo/index');
?>
W pierwszym momencie można się przera-
zić wielkością kodu, który będzie dołączony
do generowanych stron WWW. Obydwie bi-
blioteki to ponad 200KB dodatkowych da-
nych, które z pewnością nie przyspieszą star-
tu naszej aplikacji. Wielkość przesyłanych pli-
ków można jednak zmniejszyć o ponad poło-
wę, kompresując je przed przesłaniem do prze-
glądarki użytkownika (zobacz
Kompresowanie
komponentów
).
Następnym wyzwaniem jest wykrycie zmian
w formularzu – chcemy przecież reagować na
każdy znak wprowadzony przez użytkowni-
ka. Biblioteka Prototype zawiera gotowe ele-
menty, które nas wyręczą w tej czynności.
Chodzi o metodę element:
observe()
, która
pozwala na automatyczne wywołanie funk-
cji JavaScript w przypadku, gdy obserwowa-
ny element (określany za pomocą identyfika-
tora DOM ID) uległ zmianie. Nie musimy jed-
nak umieszczać kodu JavaScript w naszej apli-
kacji – CakePHP zawiera funkcję, która za-
troszczy się o inicjalizację niezbędnych struk-
tur (plik plik
app/views/todo/index.thtml
na Listingu 6.).
Jak widzimy, parametry do metody AjaxHel-
per::observeField() są przekazywane w asocja-
cyjnej tablicy $options. Ponieważ większość
metod obsługujących AJAX w CakePHP ko-
rzysta z tego sposobu, omówimy poszczegól-
ne parametry i ich znaczenie dla funkcjonowa-
nia aplikacji:
Listing 5.
Modyikacja domyślnego szablonu strony CakePHP w celu automatycznego
wczytywania bibliotek Prototype i script.aculo.us
<
?php echo $html–
>
charsetTag('UTF–8'); ?>
<
?php echo $javascript–
>
link('prototype'); ?>
<
?php
echo $javascript–
>
link('scriptaculous.js?load=effects');
?>
Kompresowanie komponentów
Czas potrzebny na przesłanie kodu HTML do użytkownika, może być znacznie zredukowany za
pomocą różnego rodzaju buforowania (cache, proxy). Innym czynnikiem, który wpływa na szyb-
kość przesyłania danych jest ich wielkość. Możemy przyspieszyć przesyłanie stron WWW, pod-
dając je wcześniejszej kompresji. Protokół HTTP/1.1 umożliwia stacjom klienckim sygnalizowanie
obsługi skompresowanych danych za pomocą nagłówka: Accept–Encoding: gzip, delate. Jeżeli
serwer WWW wykryje takie informacje, może wysłać dane skompresowane, za pomocą jednej z
metod obsługiwanej po stronie klienta.
Kompresja gzip umożliwia zmniejszenie ilości danych średnio o 60–70%, w zależności od ich
rodzaju (typu). Największe redukcje osiągniemy kompresując pliki HTML, szablony CSS oraz kod
JavaScript (dane tekstowe). Kompresja plików graicznych, plików PDF czy archiwów jest zbędna,
gdyż dane w nich zapisane już wcześniej zostały poddane kompresji.
W chwili obecnej, ponad 90% przeglądarek internetowych, używanych do przeglądania
stron WWW obsługuje kompresję danych. Od strony serwera niezbędna jest koniguracja odpo-
wiedniego modułu – w przypadku Apache 1.3 jest to mod_gzip, w Apache 2.x – mod_delate.
• $options['url']
– adres URL akcji Ca-
kePHP, która ma zostać wywołana (w na-
szym przypadku – po modyfikacji formu-
larza);
• $options['frequency']
– liczba sekund
między kolejnymi testami wykrywający-
mi zmiany;
• $options['update']
– identyfikator DOM
elementu, który ma zostać zaktualizowa-
www.phpsolmag.org
41
Rozwiązania
Listing 6.
Kod CakePHP umożliwiający reagowanie na zmiany w formularzu WWW
ny informacjami otrzymanymi w wyniku
działania akcji zawartej w $options['url'];
• $options['with']
– identyfikator DOM
elementu, który ma zostać dołączony do
żądania wysyłanego przez AJAX;
• $options['type']
– sposób komunika-
cji przeglądarki z serwerem aplikacji. Do-
myślnie przybiera wartość 'asynchronous'
(pol. asynchronicznie, w tle), jednak można
wymusić połączenie synchroniczne przez
umieszczenie wartości 'synchronous';
• $options['loading']
– kod JavaScript,
który będzie wywołany podczas pobiera-
nia danych z serwera;
• $options['loaded']
– kod JavaScript,
który będzie wywołany po zakończeniu
pobierania danych z serwera;
• $options['complete']
– kod JavaScript,
który będzie wywołany, gdy obiekt XHR
zakończył transfer danych;
• $options['condition']
– kod JavaScript
zawierający warunki, które muszą być
spełnione, zanim obiekt XHR zostanie za-
inicjalizowany;
• $options['conirm']
– tekst, który bę-
dzie wyświetlony w okienku potwierdze-
nia rozpoczęcia transferu danych przez
obiekt XHR (tzn. przed rozpoczęciem
transferu danych);
• $options['before']
– kod JavaScript,
który będzie wywołany, zanim żądanie
dostępu do danych będzie wysłane do ser-
wera WWW;
• $options['after']
– kod JavaScript, któ-
ry będzie wywołany po żądanie dostę-
pu do danych serwera WWW (zdarzenie
'after' występuje przed 'loading').
// plik app/views/todo/index.thtml
<
!–– informacja o wczytywaniu danych jest domyślnie ukryta (display: none)–
>
<
div id=
"loading"
style=
"display: none; padding: 4px; color: black;
background–color: #FAD163; width:400px"
>
<
center
>
Trwa ładowanie danych...
<
/center
>
<
/div
>
<
form onsubmit=
"return false"
>
<
p
>
<
h2
>
Wyszukiwanie:
<
/h2
>
<
input type=
"text"
id=
"search"
name=
"search"
/
>
<
/p
>
<
/form
>
<?
php
// obserwacja zmian tekstu w formularzu wyszukiwania
$options
=
array
(
'update'
=
>
'results'
,
'url'
=
>
'/todo/search'
,
'frequency'
=
>
1,
'loading'
=
>
"Element.hide
(
'results'
)
",
'complete'
=
>
"Element.show
(
'results'
)
"
)
;
echo
$ajax
–
>
observeField
(
'search'
,
$options
)
;
?>
<
!–– miejsce na wyniki poszukiwań domyślnie jest ukryte (display: none) ––
>
<
div id=
"results"
style=
"display: none; margin–top: 10px;
background–color: #EEEEEE; padding:
4px; border: 1px solid #DDDDDD; width: 400px"
><
/div
>
<
div style=
"margin–top: 10px;"
>
<?
php
// link umożliwiający czyszczenie wyników wyszukiwania
// bez przeładowania strony WWW
$options
=
array
(
'update'
=
>
'results'
,
'loading'
=
>
"Element.hide
(
'results'
)
",
'complete'
=
>
"Element.show
(
'results'
)
"
)
;
echo
$ajax
–
>
link
(
'Wyczyść listę wyszukiwania!'
,
'/todo/clear'
,
$options
)
;
?>
<
/div
>
Wracając do naszej aplikacji i Listingu 6. Za po-
mocą przedstawionego kodu będziemy moni-
torowali formularz
(search)
co jedną sekundę
($options['frequency'] => 1)
. W przypad-
ku wykrycia zmian zostanie wywołana akcja
CakePHP
, powodująca przeszukiwanie bazy da-
nych
($options['url'] => '/todo/search')
, któ-
rej wyniki zostaną wyświetlone w przeglądarce
($options['update'] => 'results')
. Kolejne
zmiany w stosunku do serwisu działającego bez
AJAX, dotyczą uniemożliwienia przeładowania
strony, w wyniku wysłaniaformularza. Standar-
dowo, klikając na przycisk
Wyślij
, powodujemy
wczytanie i wyświetlenie strony określonej pa-
rametrem
<form action="URL">.
W AJAX za-
bezpieczamy się przed tym, umieszczając para-
metr
<form onsubmit="return false">
. Nie-
wielkie modyfikacje dotknęły także metodę To-
do
Controller::search()
, która teraz umożli-
wia wybieranie wszystkich danych pasujących
do ciągu znaków wprowadzonych do formula-
rza. Jest to możliwe dzięki zastosowaniu opera-
tora SQL "LIKE" (Listing 7.).
Modyfikacja formularza spowoduje wysła-
nie zapytania do serwera WWW, który w od-
powiedzi prześle listę danych pasujących do
// plik app/views/todo/clear.thtml
// zawiera informację wyświetlaną przy czyszczeniu wyników wyszukiwania
<
center
>
Brak danych
<
/center
>
// plik app/views/todo/search.thtml
<?
php
if
(
count
(
$todo
)
>
0
)
:
?>
<
ul
>
<?
php
foreach
(
$todo
as
$task
)
:
?>
<
li
><?
php
echo
$task
[
'Task'
][
'title'
]
;
?><
/li
>
<?
php endforeach
?>
<
/ul
>
<?
php
else
:
?>
Nic nie znaleziono!
?
<?
php
endif
?>
42
06/2007
Plik z chomika:
Kapy97
Inne pliki z tego folderu:
2008.03_AJAX – kiedy i jak _[Ajax].pdf
(225 KB)
2007.06_Google Web Toolkit _[Ajax].pdf
(940 KB)
2007.06_AJAX w CakePHP _[Ajax].pdf
(545 KB)
2007.05_Formularz internetowy _[Ajax].pdf
(301 KB)
2007.02_Ankiety w stylu rankingowym z użyciem AJAX-a_[Ajax].pdf
(345 KB)
Inne foldery tego chomika:
Atak
Bazy Danych
Bezpieczenstwo
Biblioteka
Debugowanie
Zgłoś jeśli
naruszono regulamin