Ajax - Demo 7
Kertaillaan ensin hieman JavaScriptin tapahtumankäsittelyä. Hyödynnetään XMLHttpRequestia lomakkeen täytön apuna.
Rekisteröinti - Ajax
Tehdään osoitelomake, joka hakee automaattisesti postitoimipaikan tietokannasta.
Tapahtumankäsittelyä
- Tee users.jyu.fi:hin sovellukset-hakemistoon alihakemisto demo7:aa varten.
Luo uusi XHTML-dokumentti ja lomake, jossa on
kysytään nimeä, lähiosoitetta ja postinumeroa.
- index.html:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fi"> <head> <title>Syötä osoitetiedot</title> <link href="rek.css" rel="StyleSheet" type="text/css" /> </head> <body> <form action="lisaa.py" method="post"> <fieldset> <legend>Tilaajan tiedot</legend> <div> <label for="etunimi">Etunimi</label> <input type="text" id="etunimi" name="etunimi" size="40" maxlength="100"/> <span id="etunimi_req">(*)</span> </div> <div> <label for="sukunimi">Sukunimi</label> <input type="text" id="sukunimi" name="sukunimi" size="40" maxlength="100"/> <span id="sukunimi_req">(*)</span> </div> <div> <label for="lahiosoite">Lähiosoite</label> <input type="text" id="lahiosoite" name="lahiosoite" size="40" maxlength="100"/> <span id="lahiosoite_req">(*)</span> </div> <div> <label for="postinro">Postinumero</label> <input type="text" id="postinro" name="postinro" size="5" maxlength="5"/> <span id="postinro_req">(*)</span> </div> <div> <label for="postipaikka">Postitoimipaikka</label> <input type="text" id="postitoimipaikka" name="postitoimipaikka" size="40" disabled="disabled" /> <span id="postitoimipaikka_req">(*)</span> </div> <div> <label for="oppilaitosryhma">Oppilaitosryhmä</label> <select id="oppilaitosryhma" name="oppilaitosryhma"></select> </div> <div> <label for="oppilaitos">Oppilaitos</label> <select id="oppilaitos" name="oppilaitos"></select> </div> </fieldset> <p><input type="submit" name="laheta" id="laheta" value="Lähetä!" /></p> </form> </body> </html> - rek.css:
label { width: 20%; float: left; display: block; } .hidden { display: none; }
- index.html:
- Lataa samaan hakemistoon jQuery-kirjasto
ja tee uusi skriptitiedosto osoite.js. Lisää skriptit XHTML:n head-osaan
script-elementillä. - Lisää kaikille
input type="text"-lomake-elementeille tarkistus, että niissä on jotain tekstiä.- Käytä
change-tapahtumankäsittelijän lisäämiseen jQueryn bind-metodia.
- Käytä
- Jos kyseisessä elementissä on jotain, niin piilota sitä vastaava (*)-merkki
(
class-ominaisuuteen luokka hidden). Tapahtuman aiheuttaneen elementin id:n saa tapahtumankäsittelijässäthis.getAttribute("id")-metodilla. Lisää siihen "_req" ja elementin saat jQueryn$('#elementin_id')-funktiolla.- IE-yhteensopivuuden vuoksi class-attribuuttia kannattaa käsitellä elementti.className-ominaisuudesta.
- Jos jossain kentässä on vikaa, niin poista Lähetä-nappi käytöstä (disabled-attribuutti). Helpointa tässä tapauksessa on
tutkia kaikkien span-elementtien
class-attribuutteja (getElementsByTagName). Tarkistuksesta kannattaa tehdä erillinen funktio, jota kutsutaan input-elementtienchange-tapahtumankäsittelijästä. Tarkistusfunktiota kannattaa kutsua myös kertaalleenwindow.onload-funktiosta.- IE-yhteensopivuuden nimissä ei kannata käyttää DOM:in
hasAttribute-metodia.
Se ei toimi. Sen sijaan kannattaa tutkia, onko elementin
classNamearvo hidden.
- IE-yhteensopivuuden nimissä ei kannata käyttää DOM:in
hasAttribute-metodia.
Se ei toimi. Sen sijaan kannattaa tutkia, onko elementin
XMLHttpRequest ja responseText
Toteutaan postitoimipaikan valinta postinumeron perusteella.
- Lisää postinumeron change-tapahtumaan vielä toinen tapahtumankäsittelijä bind-metodilla. Tämän funktion tarkoitus on tarkistaa onko postinumeroa tietokannassa ja lisätä vastaava postitoimipaikka tekstikenttään.
- Luo demo7-hakemistoon SQLitellä uusi tietokanta, jossa on yksi taulu:
CREATE TABLE Postiosoite ( postinro CHAR(5) PRIMARY KEY NOT NULL, postitoimipaikka VARCHAR(100) NOT NULL );
- Lisää muutama testinumero:
INSERT INTO Postiosoite (postinro, postitoimipaikka) VALUES ('40740', 'Jyväskylä'); INSERT INTO Postiosoite (postinro, postitoimipaikka) VALUES ('40100', 'Jyväskylä'); INSERT INTO Postiosoite (postinro, postitoimipaikka) VALUES ('31600', 'Jokioinen'); INSERT INTO Postiosoite (postinro, postitoimipaikka) VALUES ('00100', 'Helsinki'); INSERT INTO Postiosoite (postinro, postitoimipaikka) VALUES ('33101', 'Tampere'); - Tee samaan hakemistoon mod_python-ohjelmahae_postitoimipaikka.py.
- Avaa tietokantayhteys luomaasi tietokantaan.
- Valmistele kysely:
SELECT postitoimipaikka FROM Postiosoite WHERE postinro = :postinro
- Tee muuttuja postinro ja aseta sen arvoksi
req.form.getfirst("postinro"). Kytke postinro preparoituun kyselyyn. - Hae tuloksista ensimmäinen rivi (pitäisi tulla vain 0-1 riviä).
- Palautus tehdään pelkkänä tekstinä, joten aseta sivun MIME-tyypiksi text/plain ja merkistöksi ISO-8859-1:
req.content_type = "text/plain; charset=ISO-8559-1"
- Printtaa tämän jälkeen hakutuloksista postitoimipaikka, mikäli sellainen saatiin kyselystä. Tulosta muuten tyhjä merkkijono.
- Lisää seuraavaksi postinumeron muutostapahtumaan uusi jQuery.ajax().
Sijoita
url:ksihae_postitoimipaikka.pyja lisäoptioihin:- Typeksi
GET. - HTTP-pyynnön parametreiksi (data)serialize():lla URL-enkoodaten postinro-id:llä oleva elementti.
- success-tapahtumankäsittelijäksi funktio lisaa_postitoimipaikka.
- error-tapahtumankäsittelijäksi funktio ajax_virhe.
- Typeksi
- Luo Ajax-pyynnön tapahtumankäsittelijöille funktiot. Laita molempiin jQueryn dokumentaation edellyttämät parametrit.
- Testaa Firebugin Console-ikkunasta, että XMLHttp-kutsu todella tapahtuu ja vasteena tulee järkevä postitoimipaikka.
- Aseta lisaa_postitoimipaikka-funktiossa postitoimipaikka-id:llä olevaan
kenttään (
value) saatu teksti (.responseText).- Piilota myös postitoimipaikka_req-id:llä merkitty span-elementti, jos järkevä postitoimipaikka löytyy.
- Jos vaste on tyhjä merkkijono, niin laitetaan postitoimipaikaksi tyhjä merkkijono ja laitetaan postitoimipaikan (*)-merkintä näkyväksi.
- Hyväksy myös Lähetä-nappulan käyttö, jos kaikki (*) on piilossa. Voit ajaa valmiin funktion, joka tekee tämän.
- ajax_virhe-funktio suoritetaan, jos palvelimelta tuli vasteena statuksena jokin virheilmoitus. Tulosta toistaiseksi Firebugin console.log-funktiolla vasteen statusText.
XMLHttpRequest ja responseXML
Toteutetaan vielä oppilaitoksen valinta.
Oppilaitoksien lisääminen tietokantaan
Lisää luomaasi tietokantaan vielä taulut oppilaitosryhmistä ja oppilaitoksista seuraavien SQL-lauseiden avulla:
- Hae oppilaitos.sql demo7-hakemistoon:
wget http://appro.mit.jyu.fi/sovellukset/demot/demo7/oppilaitos.sql
- Avaa sql-komentotulkki:
sqlite3 omatietokantatiedosto
- Suorita tiedostossa olevat sql-lauseet
.read oppilaitos.sql
- Poistu komentotulkista
.quit
- Lisää luku- ja kirjoitusoikeudet tietokantatiedostoon ja hakemistoon kaikille käyttäjille.
Ryhmien hakeminen tietokannasta ja lisääminen valintalistaan
- Toteuta hae_ryhmat.py-sivu, joka hakee oppilaitosryhmät tietokannasta ja tulostaa ne select-option-listana.
- Avaa yhteys tietokantaan.
- Valmistele SQL-kysely:
SELECT RyhmaID, RyhmaNimi FROM Oppilaitosryhma
- Lisää otsikkotietoihin MIME-tyypin määritys text/xml ja siihen merkistö ISO-8859-1.
- Tulosta xml-deklaraatio ja select-elementti.
Lisää select-elementtiin seuraavat attribuutit:
- xmlns="http://www.w3.org/1999/xhtml" Nimiavaruusmääritys tarvitaan, että elementti voidaan siirtää XHTML-dokumenttiin.
- id="oppilaitosryhma" Elementin valintaa varten.
- name="oppilaitosryhma" Lomakkeenkäsittelijää varten.
- Käy läpi kaikki ryhmävaihtoehdot ja tulosta kustakin option-elementti.
- Tee ensimmäisestä option-elementistä valittu (
selected="selected"). - Sijoita RyhmaID option-elementin valueksi.
- Sijoita RyhmaNimi option-elementin tekstiksi.
- Tee ensimmäisestä option-elementistä valittu (
- Tulosta
select-elementin loppu. XML-palasen voisi luoda print-lauseiden sijaan myös Pythonin xml.dom-rajapinnan avulla.
- Tee Javascript-tiedostoon uusi funktio hae_ryhmat. Tee
Ajax-kutsu, joka
pyytää hae_ryhmat.py-sivua.
Kutsu funktiota myös sivun
window.onload-funktiossa. - Lisää callback-funktio
lisaa_ryhmatonnistuneelle HTTP-pyynnölle. Aseta lisaa_ryhmat Ajax-kutsun parametrien success-attribuuttiin. Aseta error-attribuuttiin funktioajax_virhe. - Toteutetaan
lisaa_ryhmat-funktioonselect-elementin vaihto. Hae saadusta XML-vasteesta juurielementti ja sen lapsielementit (.responseXML.documentElement) importNode-funktiolla johonkin muuttujaan. Saatu select-elementti voidaan vaihtaa toiseen replaceChild-funktiolla. - IE:n puutteellinen DOM-tuki ja vanhentunut HTML-parseri aiheuttavat kuitenkin vielä ongelmia.
Tehdään vielä vaihtoehtoinen viritys IE:lle. Tutki ensin
onko käytössä
document.importNode-funktiota. Jos on, niin suorita normaali elementin vaihto. Jos ei ole, niin hae oppilaitosryhma-elementin vanhempielementti, poistaremoveChild-metodilla (vanhempielementti.removeChild(vanha_listaelementti);) entinen select-elementti ja liitä vasteen responseText vanhempielementin alle käyttämällä appendChild-metodia.
Oppilaitoksen vaihtaminen valinnan perusteella
- Toteuta hae_oppilaitokset.py-sivu, jonka ottaa GET-parametriksi oppilaitosryhma:n ja palauttaa sen
perusteella ryhmään kuuluvat oppilaitokset.
Pohjana voit käyttää hae_ryhmat.py:ta eli tee siitä ensin kopio.
- Avaa yhteys tietokantaasi.
- Valmistele SQL-kysely:
SELECT OppilaitosID, Nimi FROM Oppilaitos WHERE Oppilaitosryhma_RyhmaID = :ryhmaid
- Luo muuttuja ryhmaid, johon asetat
req.formin kautta saadun ryhmätiedon. - Kytke :ryhmaid ja ryhmaid
- Lisää otsikkotietoihin MIME-tyypin määritys text/xml (header).
- Tulosta xml-deklaraatio ja select-elementti. Lisää select-elementtiin attribuutit xmlns="http://www.w3.org/1999/xhtml" id="oppilaitos" name="oppilaitos".
- Käy läpi kaikki oppilaitosvaihtoehdot ja tulosta kustakin option-elementti.
- Tee ensimmäisestä option-elementistä valittu (
selected="selected"). - Sijoita OppilaitosID option-elementin valueksi.
- Sijoita Nimi option-elementin tekstiksi.
- Tee ensimmäisestä option-elementistä valittu (
- Tulosta
select-elementin loppu.
- Luo uusi funktio
hae_oppilaitokset. Lisää tämä tapahtumankäsittelijäksi oppilaitosryhma-valintalistallechange-tapahtumaanlisaa_ryhmat-funktion lopussa.- Mieti miksi tapahtumankäsittelijää ei voi kytkeä suoraan
window.onload-funktiossa kuten muita tarkistusfunktioita?
- Mieti miksi tapahtumankäsittelijää ei voi kytkeä suoraan
- Tee
hae_oppilaitokset-tapahtumankäsittelijässä uusi Ajax-kutsu- Laita URL:ksi
hae_oppilaitokset.py. - Laita
Type-attribuuttiin GET. - Laita
data-attribuuttiin oppilaitosryhma-id:llä olevan select-elementin valinnan arvo .serialize-funktion avulla. - Lisää
success-attribuuttiin tapahtumankäsittelijäfunktiolisaa_oppilaitokset.error-funktiona voit käyttää aiemmin luotuaajax_virhe-funktiota.
- Laita URL:ksi
- Lisää callback-funktiot tälle XMLHttpRequest-kutsulle. Virhetapauksen käsittely voi olla sama kuin edellä eli
funktio
ajax_virhe. Onnistuneessa tapauksessa (funktiolisaa_oppilaitokset) täytä oppilaitoslista saadulla XML-vasteella kuten edellä.- Itseasiassa on mahdollista käyttää samaa funktiota kuin oppilaitosryhmien kanssa,
jos ensin selvität saadusta vasteesta kumpi select-elementti pitää vaihtaa
(
.responseXML.documentElement.getAttribute("id")), mutta voit tehdä myös erilliset funktiot.
- Itseasiassa on mahdollista käyttää samaa funktiota kuin oppilaitosryhmien kanssa,
jos ensin selvität saadusta vasteesta kumpi select-elementti pitää vaihtaa
(
- Lisää vielä kertaalleen normaali
hae_oppilaitokset-kutsulisaa_ryhmat-funktion loppuun. Näin saat myös ensimmäiset oppilaitosvaihtoehdot näkyviin.

Käyttäjien kommentit