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ä

  1. 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;
      }
      
  2. Lataa samaan hakemistoon jQuery-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 jQueryn bind-metodia.
  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 saat jQueryn $('#elementin_id')-funktiolla.
    • IE-yhteensopivuuden vuoksi class-attribuuttia kannattaa käsitellä elementti.className-ominaisuudesta.
  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.

XMLHttpRequest ja responseText

Toteutaan postitoimipaikan valinta postinumeron perusteella.

  1. 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.
  2. 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
    );
  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 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.
  5. Lisää seuraavaksi postinumeron muutostapahtumaan uusi jQuery.ajax(). Sijoita url:ksi hae_postitoimipaikka.py ja 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.
  6. Luo Ajax-pyynnön tapahtumankäsittelijöille funktiot. Laita molempiin jQueryn dokumentaation edellyttämät parametrit.
  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 (.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.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.
    • Tulosta select-elementin loppu. XML-palasen voisi luoda print-lauseiden sijaan myös Pythonin xml.dom-rajapinnan avulla.
  2. Tee Javascript-tiedostoon uusi funktio hae_ryhmat. Tee Ajax-kutsu, joka pyytää hae_ryhmat.py-sivua. Kutsu funktiota myös sivun window.onload-funktiossa.
  3. Lisää callback-funktio lisaa_ryhmat onnistuneelle HTTP-pyynnölle. Aseta lisaa_ryhmat Ajax-kutsun parametrien success-attribuuttiin. Aseta error-attribuuttiin funktio ajax_virhe.
  4. Toteutetaan lisaa_ryhmat-funktioon select-elementin vaihto. Hae saadusta XML-vasteesta juurielementti ja sen lapsielementit (.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ä appendChild-metodia.

Oppilaitoksen vaihtaminen valinnan perusteella

  1. 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.
    • Tulosta select-elementin loppu.
  2. Luo uusi funktio hae_oppilaitokset. Lisää tämä tapahtumankäsittelijäksi 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
    • 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äfunktio lisaa_oppilaitokset. error-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 (.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/demo7/
© 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/>
2010-03-02 19:21:05
Informaatioteknologia - Jyväskylän yliopiston IT-tiedekunta ja avoin yliopisto