Firestore ja Fetch API - asynkroninen javascript
- Firestore
- Fetch API
- Flask ja JSON
- Flask ja XML
- Javascript, Fetch ja JSON
- Fetch ja XML
- Tilaajan lisääminen
- Async ja await
- Firebase, Firestore ja javascript-sovellus
- Lisätietoa
Tutustutaan Googlen Firestore-tietokantaan. Opetellaan myös käyttämään Javascriptin Fetch API -rajapintaa, jolla saadaan asynkronisesti kommunikoitua WWW-palvelimen kanssa. Myös Firestore-tietokantaan pääsee Javascriptilla käsiksi asynkronisen rajapinnan kautta. Tutustutaan Javascriptin Promiseihin.
Firestore
Varmista, että tietokoneellasi on uusin python. Mielellään Python 3.12.
Aloita ohjeen Get started with Cloud Firestore mukaan
- Luo uusi projekti TIES4080-ohjaus5
- Yhdistä uusi firebase-projekti edellä luomaasi projektiin
- Siirry Cloud Firestore-sivulle.
- Luo uusi Cloud Firestore -tietokanta. Tietokannan nimen on oltava default
- Valitse Start in test mode.
- Valitse tietokannan sijainniksi eur3 (europe-west)
- Asenna virtuaaliympäristöösi firebase-admin ja google-auth
pip install --upgrade firebase-admin pip install --upgrade google-auth
- Firestore täytyy vielä alustaa.
- Mene Service accounts-sivulle.
- Valitse edellä luomasi projekti.
- Valitse CREATE SERVICE ACCOUNT.
- Anna nimeksi esim. omakone.
- Valitse kohdassa Grant this service account access to project rooliksi Editor.
- Valitse edellä luomasi service accountin Actions-sarakkeesta Manage Keys
- Valitse ADD KEY ja Create new key
- Valitse avaimen tyypiksi JSON
- Tallenna avain projektisi kansioon omalle koneelle
- Luo uusi python-tiedosto alusta.py
- Liitä tiedostoon seuraava koodi:
import firebase_admin from firebase_admin import credentials from firebase_admin import firestore # Use a service account cred = credentials.Certificate('polku/tallentamaasi/json-tiedostoon/serviceAccount.json') firebase_admin.initialize_app(cred) db = firestore.client() #kokeile seuraavia doc_ref = db.collection('users').document('alovelace') doc_ref.set({ 'first': 'Ada', 'last': 'Lovelace', 'born': 1815 }) doc_ref = db.collection('users').document('aturing') doc_ref.set({ 'first': 'Alan', 'middle': 'Mathison', 'last': 'Turing', 'born': 1912 }) users_ref = db.collection('users') docs = users_ref.stream() for doc in docs: print(f'{doc.id} => {doc.to_dict()}')
- Suorita alusta.py
python alusta.py
Tarvittaessa asenna lisää kirjastoja kuten. google.auth.transport
- Liitä tiedostoon seuraava koodi:
- Nyt voit käyttää Firestorea esim. Flask-sovelluksessasi
- Firestore-sivulta pääset tutkimaan luomaasi Firestore-tietokantaa
- Luo vielä reseptitietokanta:
ruokalajit = [ {"id": 1, "nimi": "Alkuruoka", "reseptit": []}, {"id": 2, "nimi": "Pääruoka", "reseptit": [ {"nimi": "Ananas Con Carne", "kuvaus": "Tulinen", "henkilomaara": 4}, {"nimi": "Banaanicurry", "kuvaus": "Mainio kasvisruoka", "henkilomaara": 2} ]}, {"id": 3, "nimi": "Lisäkeruoka", "reseptit": []}, {"id": 4, "nimi": "Väli- tai iltapala", "reseptit": []}, {"id": 5, "nimi": "Jälkiruoka", "reseptit": []}, {"id": 6, "nimi": "Suolainen leivonnainen", "reseptit": []}, {"id": 7, "nimi": "Makea leivonnainen", "reseptit": [ {"nimi": "Tiramisu", "kuvaus": "Hieno herkku", "henkilomaara": 6}, {"nimi": "Porkkanapiirakka", "kuvaus": "Suuri herkku", "henkilomaara": 8}, {"nimi": "Rommikakku", "kuvaus": "Hyytelökakku, ei tarvi paistaa", "henkilomaara": 6}, {"nimi": "Kauralastut", "kuvaus": "Helppo ja nopea", "henkilomaara": 4} ]} ] for ruokalaji in ruokalajit: #luodaan ruokalajit käyttäen omia tunnisteita (id) rlaji = db.collection('ruokalaji').document(str( ruokalaji["id"] )) rlaji.set({ "nimi": ruokalaji["nimi"] }) reseptit = ruokalaji["reseptit"] #luodaan reseptit automaattisilla tunnisteilla # for resepti in reseptit: # db.collection("ruokalaji", rlaji.id, "reseptit").document().set({ # "nimi": resepti["nimi"], # "kuvaus": resepti["kuvaus"], # "henkilomaara": resepti["henkilomaara"] # }) #annetaan resepteillekin oma id, että on helpompi testailla #huom eri ruokalajien alla olevilla resepteillä voi olla sama id! count = 1 for resepti in reseptit: db.collection("ruokalaji", rlaji.id, "reseptit").document(str(count)).set({ "nimi": resepti["nimi"], "kuvaus": resepti["kuvaus"], "henkilomaara": resepti["henkilomaara"] }) count = count + 1
Kokeile valmiissa koodissa olevia eri tapoja luoda reseptit. Tutki miten ne ilmestyvät näkyviin Firestoren ylläpitonäkymään. Poista tuplaversiot samoista resepteistä ylläpitonäkymän kautta
- Aina ei ole käytettävissä valmista yksilöivää tietoa. Firestore osaa itse automaattisesti luoda
dokumentille uniikin id:n
>
#luo uuden dokumentin, joka on vielä tyhjä doc_ref = db.collection('tyyppi').document() #tunnisteen (id) saa suoraan id-ominaisuudesta id = doc_ref.id #dokumentin sisällön voi tallentaa set-metodilla doc_ref.set( omadata )
- Listaa kaikki ylätason kokoelmat:
collections_ref = db.collections() for col in collections_ref: documents = col.stream() for doc in documents: print(f'{doc.id} => {doc.to_dict()}')
-
Mitäs jos halutaan listata kaikki reseptit? Tämä on hankalampaa, koska
jokainen resepti asuu yksittäisen ruokalajin reseptit-kokoelmassa.
Yksittäiseen ruokalajiin kuuluvat reseptit voi hakea seuraavalla
tavalla:
Kaikkien reseptien hakemiseen tarvitaan collection group -kysely:#haetaan kaikki jälkiruokareseptit. Jälkiruokalajin id on 7 reseptit = db.collection('ruokalaji/7/reseptit').stream() for resepti in reseptit: print(f'{resepti.id} => {resepti.to_dict()}')
reseptit = db.collection_group('reseptit') docs = reseptit.stream() for doc in docs: print(f'{doc.id} => {doc.to_dict()}')
Tämä ei kuitenkaan heti onnistu vaan ohjelma kaatuu virheeseen, jossa ilmoitetaan, että kysely tarvitsee tietynlaisen indeksin. Virheilmoituksessa saa suoraan kopioitua linkin, jonka avaamalla saa luotua tarvittavan indeksin.
"The query requires a COLLECTION_GROUP_ASC index for collection reseptit and field type. You can create it here: https://console.firebase.google.com/...
Indeksin luominen kestää tovin. Jos yrität kyselyä uudelleen eikä indeksointi ole vielä valmista, saat uuden virheilmoituksen:
400 The query requires a COLLECTION_GROUP_ASC index for collection reseptit and field type. That index is not ready yet. See its status here: https://console.firebase.google.com...
- Yritä listata reseptit ruokalajeittain seuraavalla tavalla:
Alkuruoka Pääruoka Ananas Con Carne Banaanicurry Lisäkeruoka Väli- tai iltapala Jälkiruoka Suolainen leivonnainen Makea leivonnainen Tiramisu Porkkanapiirakka Rommikakku Kauralastut
Malli
ruokalajit = db.collection('ruokalaji').stream() for ruokalaji in ruokalajit: print(f'{ruokalaji.to_dict()["nimi"]}') reseptit = db.collection('ruokalaji', ruokalaji.id, 'reseptit').stream() for resepti in reseptit: print(f'\t{resepti.to_dict()["nimi"]}')
- Tarvitsetko transaktioita? Lue Transactions and batches writes
- Lisätietoa löytyy seuraavista : Admin SDK Reference ja google-cloud-firestore
Fetch API
Tutustu Javascriptin promiseihin lukemalla Using Promises -artikkeli.
Hyödynnetään Fetch-rajapintaa lomakkeen täytön apuna. Tutustu ensin ajax-luentoon ja ajax-esimerkkeihin, joissa käytetään jQuerya ja XMLHttpRequest-rajapintaa. Idea näissä on tismalleen sama kuin Fetch-rajapinnassa, mutta Fetch on tällä hetkellä järkevin rajapintavalinta käytettäväksi. Jos Javascript ei ole sinulle ennestään tuttu, niin kertaa TIEA2120 Web-käyttöliittymien ohjelmointi -kurssin ohjaustehtäviä
Malli Fetch API -versio, Lähdekoodi (javascript), Lähdekoodi (flask / python), ryhmat.xml (template)
Flask ja JSON
Ensimmäisenä tarvii valmistella palvelimella toimiva ohjelma tuottamaan JSON-muotoista dataa. Luodaan flask-ohjelma, joka palauttaa listan postitoimipaikoista JSON-muodossa.
Luo ensimmäisenä uusi SQLite3-tietokanta. Käytä oppilaitos.sql-SQL-koodia.
- Luo uusi Flask-sovellus users.jyu.fi-palvelimelle samaan tapaan kuin aiemmilla viikoilla.
- Avaa tietokantayhteys edellä luomaasi tietokantatiedostoon
- Kirjoita seuraavanlainen funktio, jotka palauttaa listan kaikista postinumeroista ja postitoimipaikoista JSON-muodossa:
from flask import Flask, session, redirect, url_for, escape, request, Response, render_template, make_response import json @app.route('/postitoimipaikat', methods=['GET']) def postitoimipaikat(): con = tietokanta() cur = con.cursor() cur.execute(""" SELECT postinro, postitoimipaikka FROM Postitoimipaikka """) data = cur.fetchall() # muunnetaan kyselyn tulos json-muotoon. Tarvitaan oma konversiofunktio row_to_json json_data = json.dumps(data, default=row_to_json) # tehdään flaskin edellyttämä response json-datasta. HTTP-koodina käytetään 200 eli OK resp = make_response(json_data, 200) # määritetään responsessa käytetty merkistö ja mediatyyppi resp.charset = "UTF-8" resp.mimetype = "application/json" return resp # muuntaa sqlite3 row -tyyppisen objektin tavalliseksi dictiksi def row_to_json(row): d = dict() for key in row.keys(): d[key] = row[key] return d
Kts. malli
- Kokeile toimiiko funktiosi. Jos toimii niin tee uusi funktio (ja osoite) postitoimipaikka,
joka palauttaa vain tietyn postinron ja postitoimipaikan sen mukaan mitä annetaan sivun parametreissa.
Esim. osoite http://users.jyu.fi/~omatunnus/cgi-bin/ties4080/ohjaus5/flask.cgi/postitoimipaikka?postinro=40740 palauttaisi vain:
[{"postinro": "40740", "postitoimipaikka": "Jyväskylä"}]
- Varmista, että sovellus ei kaadu vaikka postinroa ei annettaisikaan parametrina tai annetaan postinumero, jota ei löydy tietokannasta.
Flask ja XML
Monesti tiedonsiirtomuotona ei olekkaan JSON vaan XML.
Toteuta samaan tapaan kuin edellä kaikkien oppilaitosryhmien hakeminen, mutta palauta listaus sellaisessa XML-muodossa, että sitä voidaan suoraan käyttää HTML-dokumentissa. Käytä mediatyyppinä text/xml. Muodosta tarvittava XML Jinja-templaten avulla:
<?xml version="1.0" encoding="UTF-8"?>
<select id="{{name}}" name="{{name}}" xmlns="http://www.w3.org/1999/xhtml">
{% for o in ryhmat %}
{% if loop.first %}
<option selected="selected" value="{{o["ryhmaid"]}}">{{ o["nimi"] }}</option>
{% else %}
<option value="{{o["ryhmaid"]}}">{{ o["nimi"] }}</option>
{% endif %}
{% endfor %}
</select>
Kts. malli
Käyttäminen poikkeaa hieman aiemmista, koska myös nyt halutaan erikseen varmistaa mikä on merkistö ja mediatyyppi:
resp = make_response( render_template("ryhmat.xml",ryhmat=ryhmat, name="oppilaitosryhma"))
resp.charset = "UTF-8"
resp.mimetype = "text/xml"
return resp
Vaihtoehtoisia toteutustapoja
XML-dokumentin voit toteuttaa myös muilla tavoilla kuin Jinja-templatella. Jinja on kaikista epävarmin tapa tuottaa ehjiä XML-dokumentteja. DOM-rajapinta (minidom) tai ElementTree ovat parempia.
DOM-rajapinta
Saman voi toteuttaa myös minidom-kirjaston avulla jolloin templatea ei tarvita. Vrt. Javascript ja DOM. Kokeile:
from xml.dom.minidom import getDOMImplementation, parse, parseString
impl = getDOMImplementation()
# createDocument(nimiavaruus, juurielementti, dokumenttityyppi)
doc = impl.createDocument("http://www.w3.org/1999/xhtml", "select", None)
# pakko laittaa seuraava, koska jostakin syystä edellinen ei riitä
doc.documentElement.setAttribute("xmlns", "http://www.w3.org/1999/xhtml")
doc.documentElement.setAttribute("name", "ryhmat")
doc.documentElement.setAttribute("id", "ryhmat")
for ryhma in cur.fetchall():
option = doc.createElement("option");
txt = doc.createTextNode( ryhma['ryhmanimi'] );
option.appendChild(txt)
# minidom ei tue textContent-ominaisuutta
# li.textContent = ryhma['ryhmanimi']
option.setAttribute("value", str(ryhma["ryhmaid"]))
doc.documentElement.appendChild(option)
resp = make_response( doc.toxml('UTF-8') )
resp.charset = "UTF-8"
resp.mimetype = "text/xml"
return resp
ElementTree
Voit myös tutustua pythonin xml.etree.ElementTree-kirjastoon ja muodostaa sen avulla tarvittavan xml-dokumentin:
import xml.etree.ElementTree as ET
xml = """<?xml version="1.0" encoding="UTF-8"?><select id="ryhmat" name="ryhmat"></select>"""
root = ET.fromstring( xml )
root.attrib["xmlns"] = "http://www.w3.org/1999/xhtml"
for ryhma in cur.fetchall():
option = ET.SubElement(root, "option")
option.text = ryhma['ryhmanimi']
option.attrib['value'] = str(ryhma["ryhmaid"])
resp = make_response( ET.tostring( root, encoding="UTF-8", method="xml" ) )
Oppilaitokset
Toteuta samaan tapaan oppilaitosten hakeminen (vrt. edellä tehty yhden
postitoimipaikan hakeminen). Rajaa haettavia oppilaitoksia jollakin
oppilaitosryhmällä (ryhmaid
). Jos ryhmää ei ole annettu, silloin hae kaikki
oppilaitokset. Palauta oppilaitokset seuraavanlaisessa XML-muodossa.
Luo XML-muoto joko DOM-rajapinnan tai ElementTreen avulla.
<oppilaitokset>
<oppilaitos id="1">Helsingin kauppakorkeakoulu</oppilaitos>
<oppilaitos id="2">Helsingin yliopisto</oppilaitos>
<oppilaitos id="3">Joensuun yliopisto</oppilaitos>
...
</oppilaitokset>
Kts. malli
Javascript, Fetch ja JSON
Ota käyttöön valmis pohja. Pura samaan kansioon flask-sovelluksesi kanssa.
Muokkaa valmista osoite.js-tiedostoa. Toteutetaan postitoimipaikan valinta postinumeron perusteella.
- Lisää postinumeron change-tapahtumaan tapahtumankäsittelijä (
hae_postitoimipaikka
) - Luo hae_postitoimipaikka-funktio, jossa Fetch-rajapinnan
avulla kutsut aiemmin flaskilla tekemääsi postitoimipaikka-sivua.
function hae_postitoimipaikka() { // asetukset fetch-kutsua varten // https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch let url = new URL("http://users.jyu.fi/~omatunnus/cgi-bin/ties4080/ohjaus5/flask.cgi/postitoimipaikka"); // urliin on lisättävä ?postinumero=arvo, koska kyseessä on GET-tyyppinen request // Tämä tehdään seatchparams-objektin avulla // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams // kts. https://javascriptplayground.com/url-search-params/ url.searchParams.append("postinro", document.forms[0].elements.postinumero.value); // huomaa seuraavassa promisejen ketjutus fetch(url) .then(function(response) { // muunnetaan response-objektin sisältö jsoniksi // https://developer.mozilla.org/en-US/docs/Web/API/Response return response.json(); }) // käsitellään varsinainen tulos .then(function(data) { // data-muuttujassa saadaan javascript-objekti, josta löytyy tarvittava postitoimipaikka // huom. datan sisältö ja rakenne on juuri se mitä itse on muodostettu palvelimella try { document.forms[0].elements.postitoimipaikka.value = data[0]["postitoimipaikka"]; } // jos jotain menee pieleen niin asetetaan postitoimipaikaksi tuntematon catch (e) { document.forms[0].elements.postitoimipaikka.value = "tuntematon"; } }) // seuraava nappaa kiinni verkkovirheet ym. // Jos annettua osoitetta ei löydy, niin se ei ole virhe .catch(error => console.log('Virhe postitoimipaikan hakemisessa:', error)); }
- Kokeile toimiiko sovellus ja muuttuuko postitoimipaikan sisältö. Katso Web Developer -työkalujen Network-välilehdeltä (CTRL+SHIFT+E) mitä tapahtuu, kun vaihdat postinumerokentän sisältöä. Tarkista myös Javascript Consolen (CTRL+SHIFT+K) tulosteet.
Fetch ja XML
Toteutetaan seuraavaksi oppilaitoksen valinta oppilaitosryhmän perusteella.
- Tee Javascript-tiedostoon uusi funktio hae_ryhmat,
jossa
pyydät hae_ryhmat-sivua.
function hae_ryhmat() { // muista asynkronisuus fetch("/~omatunnus/cgi-bin/ties4080/ohjaus5/flask.cgi/hae_ryhmat") .then(function(response) { return response.text(); }) .then(function(data) { let parser = new window.DOMParser(); let apu = parser.parseFromString( data, "text/xml" ); // importoidaan saatu xml-dokumentti tähän dokumenttiin let select = document.importNode(apu.documentElement, 1); document.querySelector("#oppilaitosryhma").replaceWith(select); console.log("Oppilaitosryhmä on päivitetty"); }) .catch(error => console.log('Virhe ryhmien hakemisessa:', error)); // seuraava tulostuu _ennen_ kuin oppilaitosryhmä on päivitetty, koska fetch on asynkroninen funktio console.log("hae-ryhmat-funktion suoritus loppui"); }
- Kutsu hae_ryhmat-funktiota sivun
window.onload
-tapahtumassa, niin oppilaitosryhmälistaus päivittyy heti sivun latauduttua.
Oppilaitoksen vaihtaminen oppilaitosryhmän perusteella
- Luo Javascript-ohjelmaasi uusi funktio
hae_oppilaitokset
. Lisää tämä tapahtumankäsittelijäksi oppilaitosryhma-valintalistanchange
-tapahtumaan. Tämän lisäämisen voit tehdä vasta oppilaitosryhmän tietojen lataamisen ja sivulle päivittämisen jälkeen. Fetch-kutsut ovat asynkronisia.document.forms[0].elements.oppilaitosryhma.addEventListener("change", hae_oppilaitokset);
Sinun pitää myös flask-sovelluksen palauttamassa select-elementissä käyttää samaa id-attribuutin arvoa (oppilaitosryhma), kuin mitä valmiissa pohjadokumentissa käytetään.
- 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 Fetch-kutsu- Laita URL:ksi
oppilaitokset
. - Laita url-parametreihin
ryhmaid
-attribuuttiin valittuna olevan oppilaitosryhmän arvo - Onnistuneessa tapauksessa täytä oppilaitoslista saadulla XML-datalla.
- Epäonnistuessa tulosta virheilmoitus konsoliin
- Laita URL:ksi
- Lisää vielä
hae_oppilaitokset
-kutsulisaa_ryhmat
-funktion loppuun. Näin saat myös ensimmäiset oppilaitosvaihtoehdot näkyviin.
Tilaajan lisääminen
- Lisää lomakkeen alapuolelle listaus kaikista tietokantaan tallennetuista tilaajista. Lataa lista Fetch-kutsulla sivun latautuessa.
- Tee lomakkeesta tavalliseen tapaan toimiva eli painiketta klikattaessa
lisätään tietokantaan uusi tilaaja. Toteuta lisäys siten, että lisäyksen tekevä
flask-funktio palauttaa tiedon lisäämisen onnistumisesta tai mahdollisista
virheistä json-muodossa. Käytä lisäämisessä POST-metodia.
- Jos kaikki onnistuu, palautettava json-tiedosto on tyhjä {}
- Tarkista, että kaikkiin kenttiin on syötetty arvo
- Virheet voit listata kenttäkohtaisesti esim.
{"kentta": "virhe", "kentta2": "virhe", ...}
- Saatuasi lomakkeen ja lisäämisen toimimaan, niin muuta lisäys tapahtumaan
Fetch-kutsulla. Päivitä onnistuneen lisäämisen jälkeen sivulla oleva
tilaajalistaus. Jos lisäys epäonnistuu, niin näytä sivulla virheilmoitukset. Jos
mahdollista, niin aseta virheilmoitukset niitä vastaavien kenttien viereen.
Tämä onnistuu helposti, jos hyödynnät fiksusti kenttien nimiä ja lisäät sopivat elementit valmiiksi virheilmoituksia varten.
Koko lomakkeen sisällön saa helposti Fetch-kutsuun:
let data = new FormData( document.forms[0] ); // nyt post-metodi niin voidaan käyttää myös bodya. Luodaan tässä tarvittavat // asetusobjekti: let myInit = { method: 'POST', body: data // bodyyn määritellään käytettävä lomakedata (FormData) }; fetch("/~omatunnus/cgi-bin/ties4080/ohjaus5/flask2.cgi/lisaa_tilaaja", myInit) .then(function(response) { return response.json(); }) .then(function(data) { lisaa_success( data ); }) .catch(error => console.log('Virhe tilaajan lisäämisessä:', error)); }
Async ja await
async ja await tekevät asynkronisesta ohjelmoinnista selkeämpää.
async sana lisätään funktion nimen eteen. Tämä tekee funktiosta promisen. Tästä ei kuitenkaan ole mitään iloa ellei käytä funktion sisällä await-sanaa. await toimii vain asynkronisen funktion sisällä ja saa ohjelman odottamaan promisea.
// muunnetaan seuraava async/await-versioksi
/*
fetch("/~omatunnus/cgi-bin/ties4080/ohjaus5/flask2.cgi/tilaajat")
.then(function(response) {
return response.text();
})
.then(function(data) {
let parser = new window.DOMParser();
let xml = parser.parseFromString( data, "text/xml" );
let node = document.importNode(xml.documentElement, 1);
let id = node.getAttribute("id");
document.querySelector('#'+id).replaceWith(node);
})
.catch(error => console.log('Virhe tilaajien hakemisessa:', error));
*/
async function omafetch() {
let response = await fetch("/~omatunnus/cgi-bin/ties4080/ohjaus5/flask2.cgi/tilaajat");
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
let data = await response.text();
let parser = new window.DOMParser();
let xml = parser.parseFromString( data, "text/xml" );
let node = document.importNode(xml.documentElement, 1);
let id = node.getAttribute("id");
document.querySelector('#'+id).replaceWith(node);
console.log("Tämä tulostuu viimeisenä");
}
/* kutsutaan edellä tehtyä funktiota. Tähän ei voi laittaa await -eteen, koska
ei olla asyncilla esitellyssä funktiossa. Ohjelma ei siis jää odottamaan tätä funktiota */
omafetch()
.catch(e => {
console.log('Virhe tilaajien hakemisessa: ' + e.message);
});
console.log("Tämä tulostuu ensin");
Yritä edellä esitellyllä tavalla muuttaa ohjelmasi fetch-kutsut async/await-mallisiksi.
Malliratkaisu async/await-versio, javascript-lähdekoodi
Firebase, Firestore ja javascript-sovellus
- Project settings -sivulla voit lisätä projektiisi web-sovelluksen. Valitse Your apps-kohdasta web-sovelluksen kuvake
- Keksi joku lempinimi sovellukselle
- Älä valitse Firebase hostingia
- Valitse CDN
- Saat seuraavanlaisen valmiin koodin, jonka voit kopioida html-tiedostoon:
<script src="https://www.gstatic.com/firebasejs/8.2.9/firebase-app.js"></script> <!-- TODO: Add SDKs for Firebase products that you want to use https://firebase.google.com/docs/web/setup#available-libraries --> <script> // Your web app's Firebase configuration var firebaseConfig = { apiKey: "...", authDomain: "...", projectId: "...", storageBucket: "...", messagingSenderId: "...", appId: "..." }; // Initialize Firebase firebase.initializeApp(firebaseConfig); </script>
- Lisää vielä seuraavan skriptin lataus ennen omaa koodiasi:
<script src="https://www.gstatic.com/firebasejs/8.2.9/firebase-firestore.js"></script>
- Kokeile seuraavalla koodilla uusien dokumenttien luomista
ja dokumenttien muuttamista
let db = firebase.firestore(); db.collection("users").doc("alovelace").set({ first: "foo", last: "Lovelace", born: 1815 }) .then(() => { console.log("Document with ID: alovelace updated"); }) .catch((error) => { console.error("Error adding document: ", error); }); // Add a second document with a generated ID. db.collection("users").add({ first: "Alan", middle: "Mathison", last: "Turing", born: 1912 }) .then((docRef) => { console.log("Document written with ID: ", docRef.id); }) .catch((error) => { console.error("Error adding document: ", error); }); // tässä listauksessa eivät vielä näy edellä lisätyt, koska lisäys tapahtuu asynkronisesti db.collection("users").get().then((querySnapshot) => { querySnapshot.forEach((doc) => { console.log(`${doc.id} => ${JSON.stringify(doc.data())}`); }); });
- Käy vielä tutkimassa sovelluksesi Rules-sivua, jolla voit säätää
tietokantasi käyttöoikeuksia. Oletuksena Test-moodissa olevaan
tietokantaan on kaikilla luku- ja kirjoitusoikeus. Tuotantoversiossa
nämä pitää säätää järkevämmiksi. Lue Get started with Cloud Firestore Security Rules.
Nämä säännöt eivät koske edellä tehtyä Python-versiota.
Kirjautuminen vaaditaan:
Kaikki oikeudet kielletty:// Allow read/write access on all documents to any user signed in to the application service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if request.auth != null; } } }
Kaikilla on luku- ja kirjoitusoikeus// Deny read/write access to all users under any conditions service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if false; } } }
// Allow read/write access to all users under any conditions // Warning: **NEVER** use this rule set in production; it allows // anyone to overwrite your entire database. service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if true; } } }
Lisätietoa
Vanhoja versioita:
Yksinkertaisin ajax-malli (jquery), flask-osuuden lähdekoodi
vanha malliratkaisu
Lähdekoodi (jQuery)
Käyttäjien kommentit