Ajax - Demo 8

Kertaillaan ensin hieman JavaScriptin tapahtumankäsittelyä. Hyödynnetään XMLHttpRequestia lomakkeen täytön apuna. Tehdään muutamia kaaviota tietokantatiedoista Plotr-kirjastolla.

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 1.50 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.

Lisätehtävää

Seuraavia asioita ei vaadita viikkotehtävässä, mutta niistä voi olla hyötyä tulevaisuudessa.

Diagrammit Plotr-kirjastolla

Tehdään diagrammivalinta tietokantaan tallennettujen tietojen perusteella.

Kaavioita tietokannasta Ajaxin avulla

  1. Luo uusi SQLite-tietokanta, joka pitää kirjaa pienen kuvitteellisen sijoittajaporukan osakesalkuista.
    • Lataa luontiin tarvittavat sql-lauseet.
      wget http://appro.mit.jyu.fi/sovellukset/demot/demo8/osake.sql
    • Avaa sql-komentotulkki.
      sqlite3 osake
    • Suorita tiedostossa olevat sql-lauseet.
      .read osake.sql
    • Poistu komentotulkista.
      .quit
    • Lisää luku- ja kirjoitusoikeudet tietokantatiedostoon ja hakemistoon kaikille käyttäjille.
    • Tietokannan ER-kaavio on seuraavanlainen:

      ER-kaavio pienelle osakekannalle

  2. Hae Plotr-kirjasto ja tallenna se W:\sovellukset\demo8-hakemistoon. Pura paketti. Siirrä plotr-hakemisto lib-hakemistosta demo8-hakemistoon.
  3. Tee uusi kansio prototype151rc2 ja hae sinne uusin Prototypen versio 1.5.1 RC2.
    wget http://www.prototypejs.org/assets/2007/3/12/prototype.js
    Tässä versiossa on tuki JSON-objektien käsittelylle, jota tarvitsemme kaavioiden luomisessa.
  4. Luo uusi PHP-sivu osake.php, joka tuottaa normaalin XHTML-sivun.
  5. Lisää head-osassa script-elementeillä uusi prototype.js, Plotr.js ja oma Javascript-tiedosto diagrammit.js.
  6. Lisää PHP-sivulle body-osaan select-elementti, jossa on tietokannasta löytyvät osakkeet.
    • Avaa PDO:lla yhteys osake-tietokantaan.
    • Preparoi kysely:
      SELECT OsakeKoodi, OsakeNimi FROM Osake ORDER BY OsakeNimi ASC
    • Suorita SQL ja käy läpi tulosjoukko. Laita option-elementin tekstiksi osakkeen nimi ja value-ominaisuuteen osakkeen koodi.
    • Anna select-elementille id-ominaisuuteen arvo koodi.
  7. Lisää vielä dokumentin loppuun div-elementti, jonka id on chartdiv. Tähän luodaan kaavio Plotr-kirjastolla.
  8. Luo uusi Javascript-tiedosto diagrammit.js. Lisää window.onload-tapahtumankäsittelijässä Event.observe-metodilla koodi-select-elementille change-tapahtumankäsittelijäfunktio hae_kaaviodata.
  9. Tee funktio hae_kaaviodata, jossa teet Ajax-kutsun osakkaat.php-tiedostoon.
    • Metodiksi get.
    • Serialisoi koodi-select-elementin valinta GET-parametriksi.
    • Onnistuneen HTTP-pyynnön tapahtumankäsittelijäksi funktio lisaa_kaavio.
  10. Luo uusi PHP-sivu osakkaat.php, joka palauttaa henkilöiden osakemäärät Plotr:n ymmärtämänä JSON-objektina.
    • Laita otsikkotietoihin tyyppi text/plain ja merkistö ISO-8859-1.
    • Luo PDO:lla tietokantayhteys osake-kantaan.
    • Valmistele kysely, joka hakee parametrina tuotujen osakkeiden määrät henkilöittäin:
      SELECT Maara, HenkiloNimi FROM Omistaa, Henkilo
      WHERE Omistaa.Osake_OsakeKoodi = :koodi
      AND Omistaa.Henkilo_HenkiloID = Henkilo.HenkiloID
    • Sido GET-parametrista tullut arvo SQL-kyselyn :koodi-kohtaan.
    • Hae fetchAll-metodilla kaikki tulokset taulukkoon.
    • Luo seuraavan mallinen JSON-objekti kyselystä saatujen tietojen perusteella:
      {
        dataset: {
          'palkit': [
            [0,1210],[1,2210],[2,1234],
          ]
        },
      
        options: {
          padding: {left: 30, right: 0, top: 10, bottom: 30},
          backgroundColor: '#dbdbdb',
          yTickPrecision: 0,
          colorScheme: 'blue',
      
          xTicks: [
            {v:0, label:'Einari'}, {v:1, label:'Reijo'}, {v:2, label:'Seppo'},
          ]
        }
      }
      

      Olennaisia kohtia ovat palkit- ja xTicks-taulukot. Palkeissa on [x,y]-muodossa kerrottuna palkkien koot. x on juokseva luku, y on varsinainen osakkeiden määrä. xTicks-taulukossa taas on x-akselilla oleviin kohtiin liittyvät otsikot.

    • Testaa selaimessa, että osakkaat.php toimii erilaisilla koodi-parametreilla, esim.
      http://users.jyu.fi/~tunnus/sovellukset/demo8/osakkaat.php?koodi=NOK1V
      http://users.jyu.fi/~tunnus/sovellukset/demo8/osakkaat.php?koodi=OUT1V
  11. Palaa vielä muokkaamaan diagrammit.js-tiedostoa. Tee Ajax-pyynnön callback-funktio lisaa_kaavio. Luo uusi muuttuja vaste ja muuta responseText-merkkijono Javascript-objektiksi evalJSON()-metodilla.
  12. Poistetaan vanha div-elementti, jonka id on chartdiv. Luo uusi div-elementti ja sen sisälle epästandardi canvas-elementti:
    $('chartdiv').remove();
    new Insertion.Bottom(document.body, '<div id="chartdiv">
     <canvas id="chart" width="500" height="300"></canvas></div>');
    
    Canvas-elementin voi saada toimimaan IE6+ selaimilla excanvas.js-emulaatiokirjastolla. Lisää se script-elementillä osake.php-sivun head-osaan.
  13. Luo Plotr:llä kaavio saatuun dataan perustuen
    var bar = new Plotr.BarChart('chart', vaste.options);
    bar.addDataset(vaste.dataset);
    bar.render();
  14. Testaa kuinka kaavio vaihtuu valinnan vaihdoksen yhteydessä.

Voit kokeilla tehdä myös piirakkadiagrammia eri tyyppisten osakkeiden osuuksista salkussa eri henkilöillä.

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/
© Jukka Mäntylä (jmantyla@mit.jyu.fi) <http://www.iki.fi/jmantyla/>
Tommi Lahtonen (tommi.j.lahtonen@jyu.fi) <http://hazor.iki.fi/>
Antti Ekonoja (anjoekon@jyu.fi) <http://users.jyu.fi/~anjoekon/>
2007-04-20 10:52:34