Zaawansowane aplikacje WWW

Transkrypt

Zaawansowane aplikacje WWW
Zaawansowane aplikacje WWW - laboratorium AJAX+PHP
(Opracowanie K. Urbańska, M. Nowotka, P. Dawidczyk, M. Kalinowski)
Niniejsze ćwiczenie zawiera wprowadzenie do technologii AJAX z wykorzystaniem języka
PHP. AJAX (Asynchronous JavaScript and XML) umożliwia szybkie tworzenie aplikacji
pozwalających na interakcję użytkownika ze stroną web bez konieczności jej odświeżania.
Dzięki temu strony WWW mogą oferować podobną funkcjonalność jak aplikacje desktopowe
korzystając jedynie z możliwości przeglądarki internetowej. Interakcje AJAX-owe
wykonywane są asynchronicznie w tle, podczas gdy użytkownik może kontynuować
przeglądanie strony.
Aplikacja przygotowana w czasie tego laboratorium będzie pozwalała na przeszukiwanie
bazy danych kompozytorów poprzez udostępnienie użytkownikowi pola do wpisywania
imienia kompozytora. Pole te będzie posiadało mechanizm autouzupełniania, dzięki któremu
użytkownik nie będzie musiał dokładnie pamiętać całego imienia i nazwiska, a jedynie jego
fragment.
Oprogramowanie wykorzystane do stworzenia aplikacji:
 NetBeans w wersji 8.0.2
 Serwer Apache w wersji 2.4 (pakiet XAMPP 5.6.3)
 PHP w wersji 5.6.3 (pakiet XAMPP 5.6.3)
1. Uruchom program XAMPP oraz środowisko NetBeans. Upewnij się że uruchomione
zostały usługi Apache i MySQL, w razie potrzeby uruchom je klikając w przycisk
„Start”
2. W głównym oknie programu z menu wybierz pozycję „File” -> „New Project…”
3. Z kategorii „PHP” wybierz „PHP Application” i kliknij „Next >”
4. Wprowadź nazwę projektu MyAjaxApp, jako folder dla plików źródłowych wybierz
folder „htdocs” z katalogu w którym zainstalowany jest pakiet XAMPP (powinien
zostać domyślnie wybrany), resztę ustawień pozostaw bez zmian i kliknij „Next >”.
5. Upewnij się że w następnym oknie w opcji „Run As:” jest zaznaczone „Local Web Site
(running on local web server)” oraz że URL projektu ma postać
http://localhost/MyAjaxApp/, a następnie kliknij „Finish”.
6. Po utworzeniu projektu otwórz wygenerowany plik „index.php” i pod komentarzem
„//put your code here” umieść następujący kod (kombinacja klawiszy Ctrl+Shift+V
pozwoli wkleić sformatowany kod):
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<?php
// put your code here
echo "<h2>Hello World!</h2>";
?>
</body>
</html>
7. Przetestuj działanie aplikacji klikając prawym klawiszem myszki na nazwie projektu i
wybierając „Run”. Jeżeli serwer Apache został poprawnie zainstalowany powinniśmy
otrzymać stronę zawierającą napis „Hello World”
8. Po przetestowaniu działania aplikacji klikamy prawym klawiszem na nazwie projektu,
i z menu kontekstowego wybieramy opcję „New” -> „HTML File…”.
9. Jako nazwę pliku wpisujemy „index”. Resztę opcji zostawiamy bez zmian, klikamy na
przycisk „Finish”.
10. Zastępujemy zawartość utworzonego pliku HTML następującym kodem:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8">
<title>Auto-Completion using AJAX</title>
</head>
<body>
<h1>Auto-Completion using AJAX</h1>
</body>
</html>
11. Opcjonalnie możemy również uzupełnić stronę o tekst wyjaśniający jej działanie,
wstawiamy go zaraz pod znacznikiem <h1>:
<p>This example shows how you can do real time auto-completion using
Asynchronous JavaScript and XML (Ajax) interactions. </p>
<p>In the form below enter a name. Possible names that will be completed
are displayed below the form. For example, try typing in "Bach," "Mozart,"
or "Stravinsky," then click on one of the selections to see composer
details.</p>
12. Dodajemy do strony formularz HTML. W tym celu z palety obiektów „HTML Forms”
po prawej stronie środowiska wybieramy obiekt „Form” i przeciągamy go do
dokumentu „index”, umieszczając go między dodanymi wcześniej znacznikami „<p>”
a znacznikiem „</body>”.
13. W oknie które pojawi się po umieszczeniu obiektu „Form” uzupełniamy parametry
formularza: w polu „Action” wpisujemy „autocomplete.php”, jako typ metody
zaznaczamy „GET”, a w polu „Name” wpisujemy „autofillform”:
14. Dodajemy do strony tabelę: w tym celu z palety obiektów „HTML” przeciągamy
obiekt „Table” i wstawiamy go pomiędzy znaczniki „<form>” i „</form>”.
15. W oknie które pojawi się po umieszczeniu obiektu „Table” uzupełniamy parametry
tabeli: opcję „Rows” oraz „Columns” ustawiamy na 2, opcję „Cell Padding” ustawiamy
na 5, a resztę opcji zerujemy:
16. W pierwszym rzędzie tabeli, pomiędzy znacznikami „<td>” i „</td>” umieszczamy
następujący kod:
<td><strong>Composer Name:</strong></td>
17. W pierwszym rzędzie tabeli, pomiędzy znacznikami „<td>” i „</td>” umieszczamy
następujący kod:
<td><input type="text"
size="40"
id="complete-field"
onkeyup="doCompletion();"></td>
18. Zapisz zmiany w pliku html. Zanim przejdziemy dalej zmienimy konfigurację projektu
tak, by uruchamiany był z pliku „index.html” zamiast „index.php”. W tym celu należy
kliknąć prawym klawiszem na nazwie projektu, a następnie z menu kontekstowego
wybrać „Properties”. W otwartym oknie przechodzimy do sekcji „Run Configuration” i
zmieniamy parametr „Index File” z pliku „index.php” na „index.html”. Klikamy „OK”
by zapisać zmiany.
19. Plik „index.html” powinien w tej chwili wyglądać następująco:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Auto-Completion using AJAX</title>
</head>
<body>
<h1>Auto-Completion using AJAX</h1>
<p>This example shows how you can do real time auto-completion
using Asynchronous
JavaScript and XML (Ajax) interactions.</p>
<p>In the form below enter a name. Possible names that will be
completed are displayed
below the form. For example, try typing in "Bach," "Mozart," or
"Stravinsky,"
then click on one of the selections to see composer
details.</p>
<form name="autofillform" action="autocomplete.php"><table
border="0" cellpadding="5">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Composer Name:</strong></td>
<td><input type="text"
size="40"
id="complete-field"
onkeyup="doCompletion();"></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</form>
</body>
</html>
20. Przygotujemy teraz skrypt pozwalający na autouzupełnianie wpisywanych przez
użytkownika imion. W tym celu klikamy prawym przyciskiem myszy na nazwie
projektu i wybieramy „New” -> „JavaScript File…”.
21. W oknie które się pojawi wpisujemy tylko nazwę pliku „javascript”, po czym klikamy
„Finish”.
22. Zastępujemy zawartość nowo utworzonego pliku następującym kodem:
var req;
var isIE;
function init() {
completeField = document.getElementById("complete-field");
}
function doCompletion() {
var url = "autocomplete.php?action=complete&id=" +
escape(completeField.value);
req = initRequest();
req.open("GET", url, true);
req.onreadystatechange = callback;
req.send(null);
}
function initRequest() {
if (window.XMLHttpRequest) {
if (navigator.userAgent.indexOf('MSIE') != -1) {
isIE = true;
}
return new XMLHttpRequest();
} else if (window.ActiveXObject) {
isIE = true;
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
23. Modyfikujemy plik „index.html” : w sekcji „<head>” dołączamy utworzony skrypt:
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Auto-Completion using AJAX</title>
<script type="text/javascript" src="javascript.js"></script>
</head>
24. W znaczniku „<body>” dodajemy odwołanie do funkcji „init()”:
<body onload="init()">
25. Możemy teraz przetestować działanie aplikacji. W tym celu klikamy prawym
klawiszem myszy na nazwie projektu i wybieramy opcję „Run”. Efektem powinna być
strona zawierająca tytuł, dwa paragrafy opisu oraz pole do wprowadzania tekstu
opisane „Composer Name”.
26. W kolejnym kroku przygotujemy serwerową część aplikacji. W tym celu tworzymy
nową klasę PHP „Composer” klikając prawym klawiszem na nazwie projektu i
wybierając „New” -> „PHP Class…”.
27. W oknie które się pojawi wpisujemy „Composer” w polu „File Name”. Resztę opcji
pozostawiamy bez zmian, klikamy „Finish”.
28. Zawartość nowo utworzonego pliku zastępujemy następującym kodem:
<?php
class Composer {
public $id;
public $firstName;
public $lastName;
public $category;
function __construct($id, $firstName, $lastName, $category) {
$this->id = $id;
$this->firstName = $firstName;
$this->lastName = $lastName;
$this->category = $category;
}
}
?>
29. Podobnie jak poprzednio tworzymy klasę „ComposerData”, a zawartość
wygenerowanego pliku zastępujemy następującym kodem:
<?php
require "Composer.php";
class ComposerData {
public $composers;
function __construct() {
$this->composers = array(
new Composer("1", "Johann Sebastian", "Bach", "Baroque"),
new Composer("2", "Arcangelo", "Corelli", "Baroque"),
new Composer("3", "George Frideric", "Handel", "Baroque"),
new Composer("4", "Henry", "Purcell", "Baroque"),
new Composer("5", "Jean-Philippe", "Rameau", "Baroque"),
new Composer("6", "Domenico", "Scarlatti", "Baroque"),
new Composer("7", "Antonio", "Vivaldi", "Baroque"),
new Composer("8", "Ludwig van", "Beethoven", "Classical"),
new Composer("9", "Johannes", "Brahms", "Classical"),
new Composer("10", "Francesco", "Cavalli", "Classical"),
new Composer("11", "Fryderyk Franciszek", "Chopin",
"Classical"),
new Composer("12", "Antonin", "Dvorak", "Classical"),
new Composer("13", "Franz Joseph", "Haydn", "Classical"),
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
new
);
}
}
?>
Composer("14",
Composer("15",
Composer("16",
Composer("17",
Composer("18",
Composer("19",
Composer("20",
Composer("21",
Composer("22",
Composer("23",
Composer("24",
Composer("25",
Composer("26",
Composer("27",
Composer("28",
Composer("29",
Composer("30",
Composer("31",
Composer("32",
Composer("33",
Composer("34",
Composer("35",
Composer("36",
Composer("37",
Composer("38",
Composer("39",
Composer("40",
Composer("41",
Composer("42",
Composer("43",
Composer("44",
Composer("45",
Composer("46",
Composer("47",
Composer("48",
Composer("49",
Composer("50",
"Gustav", "Mahler", "Classical"),
"Wolfgang Amadeus", "Mozart", "Classical"),
"Johann", "Pachelbel", "Classical"),
"Gioachino", "Rossini", "Classical"),
"Dmitry", "Shostakovich", "Classical"),
"Richard", "Wagner", "Classical"),
"Louis-Hector", "Berlioz", "Romantic"),
"Georges", "Bizet", "Romantic"),
"Cesar", "Cui", "Romantic"),
"Claude", "Debussy", "Romantic"),
"Edward", "Elgar", "Romantic"),
"Gabriel", "Faure", "Romantic"),
"Cesar", "Franck", "Romantic"),
"Edvard", "Grieg", "Romantic"),
"Nikolay", "Rimsky-Korsakov", "Romantic"),
"Franz Joseph", "Liszt", "Romantic"),
"Felix", "Mendelssohn", "Romantic"),
"Giacomo", "Puccini", "Romantic"),
"Sergei", "Rachmaninoff", "Romantic"),
"Camille", "Saint-Saens", "Romantic"),
"Franz", "Schubert", "Romantic"),
"Robert", "Schumann", "Romantic"),
"Jean", "Sibelius", "Romantic"),
"Bedrich", "Smetana", "Romantic"),
"Richard", "Strauss", "Romantic"),
"Pyotr Il'yich", "Tchaikovsky", "Romantic"),
"Guiseppe", "Verdi", "Romantic"),
"Bela", "Bartok", "Post-Romantic"),
"Leonard", "Bernstein", "Post-Romantic"),
"Benjamin", "Britten", "Post-Romantic"),
"John", "Cage", "Post-Romantic"),
"Aaron", "Copland", "Post-Romantic"),
"George", "Gershwin", "Post-Romantic"),
"Sergey", "Prokofiev", "Post-Romantic"),
"Maurice", "Ravel", "Post-Romantic"),
"Igor", "Stravinsky", "Post-Romantic"),
"Carl", "Orff", "Post-Romantic"),
30. Następnie tworzymy skrypt odpowiedzialny za obsługę przycodzących zapytań
„autocomplete”. Do tego celu wykorzystamy istniejący plik „index.php”. Pierwszym
krokiem jest zmiana jego nazwy na „autocomplete.php” poprzez kliknięcie drugim
klawiszem na jego nazwę w drzewie projektu i wybranie opcji „Rename”. W oknie
które się pojawi wpisujemy „autocomplete”.
31. Po zmianie nazwy pliku przechodzimy do jego edycji, i zastępujemy jego zawartość
poniższym kodem:
<?php
require_once("ComposerData.php");
session_start();
$composerData = new ComposerData();
$composers = $composerData->composers;
$results = array();
$namesAdded = false;
// simple matching for start of first or last name, or both
if (isset($_GET['action']) && $_GET['action'] == "complete") {
foreach ($composers as $composer) {
if (!is_numeric($_GET['id']) &&
// if id matches first name
(stripos($composer->firstName, $_GET['id']) === 0 ||
// if id matches last name
stripos($composer->lastName, $_GET['id']) === 0) ||
// if id matches full name
stripos($composer->firstName . " " . $composer->lastName,
$_GET['id']) === 0) {
$results[] = $composer;
}
}
// prepare xml data
if (sizeof($results) != 0) {
header('Content-type: text/xml');
echo "<composers>";
foreach ($results as $result) {
echo "<composer>";
echo "<id>" . $result->id . "</id>";
echo "<firstName>" . $result->firstName . "</firstName>";
echo "<lastName>" . $result->lastName . "</lastName>";
echo "</composer>";
}
echo "</composers>";
}
}
// if user chooses from pop-up box
if (isset($_GET['action']) && isset($_GET['id']) && $_GET['action'] ==
"lookup") {
foreach ($composers as $composer) {
if ($composer->id == $_GET['id']) {
$_SESSION ["id"] = $composer->id;
$_SESSION ["firstName"] = $composer->firstName;
$_SESSION ["lastName"] = $composer->lastName;
$_SESSION ["category"] = $composer->category;
header("Location: composerView.php");
}
}
}
?>
32. Następnie tworzymy stronę odpowiedzialną za wyświetlanie pełnej informacji o
wybranym kompozytorze. W tym celu tworzymy nowy plik PHP klikając prawym
klawiszem na nazwie projektu i wybierając „New” -> „PHP File…”.
33. W oknie które się wyświetli wpisujemy w polu „File Name” „composerView”, resztę
wartości pozostawiając bez zmian i klikając „Finish”.
34. Zawartość nowo utworzonego pliku zastępujemy poniższym kodem:
<?php
session_start();
$id = $_SESSION ["id"];
$firstName = $_SESSION ["firstName"];
$lastName = $_SESSION ["lastName"];
$category = $_SESSION ["category"];
?>
<html>
<head>
<title>Composer Information</title>
<link rel="stylesheet" type="text/css" href="stylesheet.css">
</head>
<body>
<table>
<tr>
<th colspan="2">Composer Information</th>
</tr>
<tr>
<td>First Name: </td>
<td><?php echo $firstName; ?></td>
</tr>
<tr>
<td>Last Name: </td>
<td><?php echo $lastName; ?></td>
</tr>
<tr>
<td>ID: </td>
<td><?php echo $id; ?></td>
</tr>
<tr>
<td>Category: </td>
<td><?php echo $category; ?></td>
</tr>
</table>
<p>Go back to <a href="index.html" class="link">application
home</a>.</p>
</body>
</html>
35. Następnym krokiem jest utworzenie funkcji „callbackowej” która pozwoli na obsługę
odpowiedzi serwera. Przechodzimy do edycji pliku „javascript.js” i dopisujemy do
niego następującą funkcję:
function callback() {
if (req.readyState == 4) {
if (req.status == 200) {
parseMessages(req.responseXML);
}
}
}
36. Zwróć uwagę na zmienną „req.readyState” z poprzedniego fragmentu kodu. Oznacza
ona stan w jakim znajduje się interakcja, i może przyjmować wartości w zakresie 0-4
oznaczające odpowiednio stan niezainicjowany, ładowanie w trakcie, ładowanie
zakończone, gotowość do interakcji oraz zakończenie interakcji.
37. W pliku „index.html” edytujemy drugi rząd w wcześniej dodanej tabeli:
<tbody>
<tr>
<td><strong>Composer Name:</strong></td>
<td><input type="text"
size="40"
id="complete-field"
nkeyup="doCompletion();"></td>
</tr>
<tr>
<td id="auto-row" colspan="2">
<table class="popupBox" style="display:
none"></table>
</td>
</tr>
</tbody>
38. W pliku „javascript.js” na górze dodajemy deklaracje zmiennych:
var completeField;
var completeTable;
var autoRow;
39. Następnie w tym samym pliku modyfikujemy funkcję „init()”:
function init() {
completeField = document.getElementById("complete-field");
completeTable = document.createElement("table");
completeTable.setAttribute("class", "popupBox");
completeTable.setAttribute("style", "display: none");
autoRow = document.getElementById("auto-row");
autoRow.appendChild(completeTable);
completeTable.style.top = getElementY(autoRow) + "px";
}
40. Dodajemy również funkcję „appendComposer()”:
function appendComposer(firstName, lastName, composerId) {
var row;
var cell;
var linkElement;
if (isIE) {
completeTable.style.display = 'block';
row = completeTable.insertRow(completeTable.rows.length);
cell = row.insertCell(0);
} else {
completeTable.style.display = 'table';
row = document.createElement("tr");
cell = document.createElement("td");
row.appendChild(cell);
completeTable.appendChild(row);
}
cell.className = "popupCell";
linkElement = document.createElement("a");
linkElement.className = "popupItem";
linkElement.setAttribute("href", "autocomplete.php?action=lookup&id=" +
composerId);
linkElement.appendChild(document.createTextNode(firstName + " " +
lastName));
cell.appendChild(linkElement);
}
41. Następnie funkcję „clearTable()”:
function clearTable() {
if (completeTable.getElementsByTagName("tr").length > 0) {
completeTable.style.display = 'none';
for (loop = completeTable.childNodes.length - 1; loop >= 0; loop--)
{
completeTable.removeChild(completeTable.childNodes[loop]);
}
}
}
42. Oraz funkcję „getElementY(element)”:
function getElementY(element) {
var targetTop = 0;
if (element.offsetParent) {
while (element.offsetParent) {
targetTop += element.offsetTop;
element = element.offsetParent;
}
} else if (element.y) {
targetTop += element.y;
}
return targetTop;
}
43. Dokonujemy modyfikacji funkcji „callback()”, tak aby dokonywała czyszczenia tablicy
przed przetwarzanie odpowiedzi serwera. Pozwala to na usunięcie poprzednich
wyników wyszukiwania i zastąpienie ich nowymi:
function callback() {
clearTable();
if (req.readyState == 4) {
if (req.status == 200) {
parseMessages(req.responseXML);
}
}
}
44. Na koniec dodajemy do pliku „javascript.js” funkcję odpowiedzialną za przetwarzanie
odpowiedzi serwera:
function parseMessages(responseXML) {
// no matches returned
if (responseXML == null) {
return false;
} else {
var composers = responseXML.getElementsByTagName("composers")[0];
if (composers.childNodes.length > 0) {
completeTable.setAttribute("bordercolor", "black");
completeTable.setAttribute("border", "1");
for (loop = 0; loop < composers.childNodes.length; loop++) {
var composer = composers.childNodes[loop];
var firstName =
composer.getElementsByTagName("firstName")[0];
var lastName =
composer.getElementsByTagName("lastName")[0];
var composerId = composer.getElementsByTagName("id")[0];
appendComposer(firstName.childNodes[0].nodeValue,
lastName.childNodes[0].nodeValue,
composerId.childNodes[0].nodeValue);
}
}
}
}
45. Możemy teraz uruchomić projekt (opcja „Run” po kliknięciu prawym klawiszem na
nazwie projektu) i sprawdzić działanie autouzupełniania.
46. Dla ulepszenia wyglądu strony możemy do niej dołączyć plik .css. W tym celu klikamy
prawym klawiszem na nazwie projektu i z menu „New” wybieramy „Cascading Style
Sheet…”. W oknie które się pojawi jako nazwę podajemy „stylesheet”, resztę opcji
pozostawiając bez zmian. Klikamy „Finish”.
47. Zawartość nowo utworzonego pliku zastępujemy następującym kodem:
body {
font-family: sans-serif;
font-size: smaller;
padding: 50px;
color: #555;
width: 650px;
}
h1 {
letter-spacing: 6px;
font-size: 1.6em;
color: #be7429;
font-weight: bold;
}
h2 {
text-align: left;
letter-spacing: 6px;
font-size: 1.4em;
color: #be7429;
font-weight: normal;
width: 450px;
}
table {
width: 550px;
padding: 10px;
background-color: #c5e7e0;
}
td {
padding: 10px;
}
a {
color: #be7429;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.popupBox {
position: absolute;
top: 170px;
left: 140px;
}
.popupCell {
background-color: #fffafa;
}
.popupCell:hover {
background-color: #f5ebe9;
}
.popupItem {
color: #333;
text-decoration: none;
font-size: 1.2em;
}
48. Pozostało nam dołączyć utworzony plik .css w sekcji „<head>” pliku „index.html”:
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Auto-Completion using AJAX</title>
<script type="text/javascript" src="javascript.js"></script>
<link rel="stylesheet" type="text/css" href="stylesheet.css">
</head>
49. Możemy teraz uruchomić projekt i ostatecznie przetestować jego działanie.