Ajax - Demo 8

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ä

  1. Tee users.jyu.fi:hin sovellukset-hakemistoon alihakemisto demo8: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.php" 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;
      }
      
  2. Lataa samaan hakemistoon Prototype-kirjasto ja tee uusi skriptitiedosto osoite.js. Lisää skriptit XHTML:n head-osaan script-elementillä.
  3. Lisää kaikille input type="text"-lomake-elementeille tarkistus, että niissä on jotain tekstiä.
    • Käytä change-tapahtumankäsittelijän lisäämiseen Prototypen Event.observe-metodia.
      • Jos haluat tehdä koodista IE-yhteensopivaa, niin laita viimeisena parametrina olevaan funktioon vielä lisämääre
        funktionnimi.bindAsEventListener($('elementin_id'))

        Tällöin tapahtumankäsittelijässä this viittaa oikealla tavalla tapahtuman aiheuttaneeseen elementtiin.

  4. 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 saa Prototypen $('elementin_id')-funktiolla.
    • IE-yhteensopivuuden vuoksi class-attribuuttia kannattaa käsitellä elementti.className-ominaisuudesta.
    • Tapahtumankäsittelijässä ei kannata käyttää myöskään event-objektia, sillä toteutus vaihtelee selaimittain (ts. IE on omalla polulla). Oikein toimiva this pitäisi riittää tässä tapauksessa. Kts. Supporting Three Event Models at Once.
  5. 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-elementtien change-tapahtumankäsittelijästä. Tarkistusfunktiota kannattaa kutsua myös kertaalleen window.onload-funktiosta.
    • IE-yhteensopivuuden nimissä ei kannata käyttää DOM:in hasAttribute-metodia. Se ei toimi. Sen sijaan kannattaa tutkia, onko elementin className arvo hidden. Jos hasAttribute on välttämätön, niin kannattaa vilkaista Prototypen Elements.Methods.Simulated.hasAttribute -metodia.

XMLHttpRequest ja responseText

Toteutaan postitoimipaikan valinta postinumeron perusteella.

  1. Lisää postinumeron change-tapahtumaan vielä toinen tapahtumankäsittelijä Event.observe-metodilla. Tämän funktion tarkoitus on tarkistaa onko postinumeroa tietokannassa ja lisätä vastaava postitoimipaikka tekstikenttään.
  2. Luo demo8-hakemistoon SQLitellä uusi tietokanta, jossa on yksi taulu:
    CREATE TABLE Postiosoite (
    postinro CHAR(5) PRIMARY KEY NOT NULL,
    postitoimipaikka VARCHAR(100) NOT NULL
    );
  3. 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');
  4. Tee samaan hakemistoon PHP-tiedosto hae_postitoimipaikka.php.
    • Avaa PDO:lla tietokantayhteys luomaasi tietokantaan.
    • Preparoi kysely:
      SELECT postitoimipaikka FROM Postiosoite WHERE postinro = :postinro
    • Tee muuttuja $postinro ja aseta sen arvoksi $_REQUEST['postinro']. Kytke $postinro preparoituun kyselyyn.
      • Huomaa, että jos kyseessä olisi tekstiä sisältävä muuttuja, niin merkistö pitäisi vaihtaa iconv-funktiolla UTF-8:sta ISO-8859-1:seen.
    • 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:
      header("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.
  5. Lisää seuraavaksi postinumeron muutostapahtumaan uusi Ajax.Request. Sijoita url:ksi hae_postitoimipaikka.php ja lisäoptioihin:
    • Metodiksi get.
    • HTTP-pyynnön parametreiksi Form.Element.serialize:lla URL-enkoodaten postinro-id:llä oleva elementti.
    • onSuccess-tapahtumankäsittelijään funktio lisaa_postitoimipaikka.
    • onFailure-tapahtumankäsittelijään funktio ajax_virhe.
  6. Luo Ajax-pyynnön tapahtumankäsittelijöille funktiot. Laita molempiin parametriksi transport.
  7. Testaa Firebugin Console-ikkunasta, että XMLHttp-kutsu todella tapahtuu ja vasteena tulee järkevä postitoimipaikka.
  8. Aseta lisaa_postitoimipaikka-funktiossa postitoimipaikka-id:llä olevaan kenttään (value) saatu teksti (transport.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.
  9. 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:

Ryhmien hakeminen tietokannasta ja lisääminen valintalistaan

  1. Toteuta hae_ryhmat.php-sivu, joka hakee oppilaitosryhmät tietokannasta ja tulostaa ne select-option-listana.
    • Avaa yhteys PDO:lla.
    • Preparoi SQL-kysely:
      SELECT RyhmaID, RyhmaNimi FROM Oppilaitosryhma
    • Lisää otsikkotietoihin MIME-tyypin määritys text/xml (header) 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.
    • Tulosta select-elementin loppu. XML-palasen voisi luoda print-lauseiden sijaan myös PHP:n DOM-rajapinnan avulla (kts. luento 17)
  2. Tee Javascript-tiedostoon uusi funktio hae_ryhmat. Tee Ajax-kutsu, joka pyytää hae_ryhmat.php-sivua. Kutsu funktiota myös sivun window.onload-funktiossa.
  3. Lisää callback-funktio lisaa_ryhmat(transport) onnistuneelle HTTP-pyynnölle. Lisää lisaa_ryhmat Ajax-kutsun parametrien onSuccess-attribuuttiin. Lisää onFailure-attribuuttiin funktio ajax_virhe.
  4. Toteutetaan lisaa_ryhmat-funktioon select-elementin vaihto. Hae saadusta XML-vasteesta juurielementti ja sen lapsielementit (transport.responseXML.documentElement) importNode-funktiolla johonkin muuttujaan. Saatu select-elementti voidaan vaihtaa toiseen replaceChild-funktiolla.
  5. 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, poista removeChild-metodilla (vanhempielementti.removeChild(vanha_listaelementti);) entinen select-elementti ja liitä vasteen responseText vanhempielementin alle käyttämällä Prototypen Insertion.Bottom-funktiota.

Oppilaitoksen vaihtaminen valinnan perusteella

  1. Toteuta hae_oppilaitokset.php-sivu, jonka ottaa GET-parametriksi oppilaitosryhma:n ja palauttaa sen perusteella ryhmään kuuluvat oppilaitokset. Pohjana voit käyttää hae_ryhmat.php:ta eli tee siitä ensin kopio.
    • Avaa yhteys PDO:lla tietokantaasi.
    • Valmistele SQL-kysely:
      SELECT OppilaitosID, Nimi FROM Oppilaitos WHERE Oppilaitosryhma_RyhmaID = :ryhmaid
    • Luo muuttuja $ryhmaid, johon asetetat $_GET-taulukosta saadun ryhmätiedon.
    • Kytke :ryhmaid ja $ryhmaid bindParam-funktiolla.
    • 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.
    • Tulosta select-elementin loppu.
  2. Luo uusi funktio hae_oppilaitokset. Lisää tämä tapahtumankäsittelijäksi Event.observe-metodilla oppilaitosryhma-valintalistalle change-tapahtumaan lisaa_ryhmat-funktion lopussa.
    • Mieti miksi tapahtumankäsittelijää ei voi kytkeä suoraan window.onload-funktiossa kuten muita tarkistusfunktioita?
  3. Tee hae_oppilaitokset-tapahtumankäsittelijässä uusi Ajax-kutsu Prototypen Ajax.Request-metodilla.
    • Laita URL:ksi hae_oppilaitokset.php.
    • Laita method-attribuuttiin get.
    • Laita parameters-attribuuttiin oppilaitosryhma-id:llä olevan select-elementin valinnan arvo Form.Element.serialize-funktion avulla.
    • Lisää onSuccess-attribuuttiin tapahtumankäsittelijäfunktio lisaa_oppilaitokset. onFailure-funktiona voit käyttää aiemmin luotua ajax_virhe-funktiota.
  4. Lisää callback-funktiot tälle XMLHttpRequest-kutsulle. Virhetapauksen käsittely voi olla sama kuin edellä eli funktio ajax_virhe. Onnistuneessa tapauksessa (funktio lisaa_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 (transport.responseXML.documentElement.getAttribute("id")), mutta voit tehdä myös erilliset funktiot.
  5. Lisää vielä kertaalleen normaali hae_oppilaitokset-kutsu lisaa_ryhmat-funktion loppuun. Näin saat myös ensimmäiset oppilaitosvaihtoehdot näkyviin.

Käyttäjien kommentit

Kommentoi tätä sivua Lisää uusi kommentti
Kurssimateriaalien käyttäminen kaupallisiin tarkoituksiin tai opetusmateriaalina ilman lupaa on ehdottomasti kielletty!
http://appro.mit.jyu.fi/sovellukset/demot/demo8/
© Antti Ekonoja (antti.j.ekonoja@jyu.fi) <http://users.jyu.fi/~anjoekon/>
Tommi Lahtonen (tommi.j.lahtonen@jyu.fi) <http://hazor.iki.fi/>
Jukka Mäntylä (jmantyla@iki.fi) <http://www.iki.fi/jmantyla/>
2008-12-17 15:03:42
Informaatioteknologia - Jyväskylän yliopiston IT-tiedekunta ja avoin yliopisto