Appearance
ToDo javaScript client
Projektin aloitus ja kehitysympäristön valmistelu
Luo Client projektille uusi repositorio GitLabiin, samalla tavalla kuten API tehtävässäkin.
Nimeä repositorio haluamallasi tavalla, esim. todo-client.
Ohjelmistot
Node.js
Useiden modernien web-sovelluskehysten kuten Reactin tai Vuen kehitysympäristöt toimivat Node.js ohjelmiston päällä.
Node.js on JavaScript koodin suorittamisen ympäristö muualla kuin verkkoselaimissa. Se mahdollistaa ohjelmoinnin JavaScriptillä alustariippumattomasti ja antaa pääsyn JavaScript koodilla esimerkiksi tiedostojärjestelmään tai verkkoliikenteeseen.
Noden mukana asentuvat npm ja npx, ne ovat noden komentorivityökaluja JavaScript pakettien eli kolmannen osapuolen koodikirjastojen hallintaan.
Node.js
Lataa ja asenna Node.js LTS versio.
npm paketit
Aina kun kloonaat tai otat käyttöön uuden node-projektin niin asenna ensimmäiseksi npm paketit.
Suorita komento samasta polusta missä package.json tiedosto projektissa sijaitsee.
bash
npm install
Komento asentaa package.json tiedostossa listatut koodikirjastot (dependencies) node_modules kansioon.
Vaatimusmäärittely
Toteuta client-sovellus joka hyödyntää tehtävässä 1 rakennettua ToDo-rajapintaa.
ToDo-Client tarvitsee käyttöliittymän minkä kautta käyttäjä voi ToDo-API:a hyödyntämällä:
- listata kaikki todo-tehtävät (3p)
- hakea listassa näytettäväksi suoritetut todo-tehtävät (done=true), tekemättömät (done=false) tai kaikki todo-tehtävät kerralla (4p)
- luoda uuden todo-tehtävän, antamalla todo-tehtävälle otsikon ja kuvauksen (title ja description) (3p)
- poistaa todo-tehtävän (3p)
- muokata olemassa olevaa todo-tehtävää (4p)
- asettaa todo-tehtävän status tehdyksi tai tekemättömäksi (3p)
Tämän lisäksi client-sovellus hyödyntää jotain/joitain 3. osapuolen rajapintoja TAI client-sovellus hyödyntää itse toteuttamaasi rajapinnan lisäominaisuutta. (10p)
- Toteuta tarvittavat javaScript funktiot joita hyödyntämällä muualla koodissa voit hakea datan visualisoitavaksi käyttöliittymässä.
- Tässä voit käyttää tehtävässä 1 tekemääsi lisäominaisuutta tai käyttää suoraan jotain 3. osapuolen rajapintaa clientilta.
- Huomioi 3. osapuolen rajapinnan tarkoituksenmukaisuus ToDo-sovelluksessa, eli ominaisuuden täytyy tuoda jotain lisäarvoa sovelluksen toimintaan.
- Tähän tulossa myöhemmin vielä lista esimerkkejä
Esimerkkejä Reactilla tehdyistä ToDo-sovelluksista
Client tehtävän vaihtoehdot, A ja B
Tehtävä 2 on jaettu kahteen eri polkuun, A ja B, joista valitset toisen (!).
Vaihtoehto A: Käy tehtävän yksityiskohtaisemmin läpi, soveltavat tehtävät osana kokonaisuutta.
- Osittain ohjattu osuus, sisältää pienempiä soveltavia tehtäviä 20p
- Kokonaan soveltava osuus 10p
Vaihtoehto B: Suunnattu niille opiskelijoille joilla on jo aiheesta enemmän kokemusta tai halua haastaa itseään, tämä vaihtoehto antaa vapauden valita itse käytettävät teknologiat ja menetelmät.
- Tehtävä on 100% soveltava, 30p
- Tehtävän tulee noudattaa annettua vaatimusmäärittelyä
- Voit hyödyntää A osion ohjattua osuutta
- Pyydä rohkeasti apuja opintojakson ohjaajalta
- Hyödynnä opintojakson Teams tilaa vinkkien jakamisessa ja tukikanavana!
Vaihtoehto B:lle ei tule erikseen vastaavaa läpikäyntiä kuten A:lle tästä alaspäin
Tehtäväpolku A
React projektin luominen
- Kloonaa projektille luomasi repositorio koneellesi.
- Siirry komentorivillä projektikansion juuren sijaintiin
- Luodaan uusi React projekti, suorita seuraava komento komentoriviltä/terminaalista:
Huom! Varmista että suoritat komennon projektikansion juuressa. Piste komennon perässä tarkoittaa että projektin tiedostot luodaan samaan sijaintiin mistä komento suoritetaan.
npm create vite@latest .
Tee seuraavat valinnat:
Suorita "npm install" komento, tämä hakee ja asentaa projektin package.json tiedostossa listatut koodikirjatot node_modules kansioon.
Komento "npm run dev" käynnistää React-projektin kehityspalvelimen. Konsoliin tulostuu URL josta voi selaimella avata aloitusprojektin sivun. Kehityspalvelin käyttää ns. "Hot-reload" menetelmää eli aina kun tiedoston tallentaa, kehityspalvelin lataa muutokset automaattisesti.
Luodun React projektin alustus
HUOM!
Jos olet jo luennolla lisännyt projektiisi läpi käydyn esimerkin, säilytä tekemäsi koodi App.jsx tiedostossa, sama käydään tässä myös läpi myöhemmin!
Poista "template koodi" eli siivotaan projektista kaikki ylimääräinen niin että voidaan aloittaa ns. puhtaalta pöydältä.
- Poista App.css tiedosto
- Poista tyylittelyt index.css tiedostosta eli jätä pelkkä tyhjä tiedosto
- Muokkaa App.jsx tiedoston koodi seuraavanlaiseksi ja poista importit:
App.jsx
jsx
function App() {
return <div></div>
}
// export default on yksi tapa kuinka javaScriptissä voidaan merkata
// funktio, muuttuja tai tässä tapauksessa React funktio-komponentti niin
// että sen voi toisessa tiedostossa tuoda käyttöön
// import:ia käyttäen. ks. main.jsx tiedoston import App from './App'
export default App
- "Siivouksen" jälkeen kansiorakenne näyttää VS Codessa tältä:
Peruspohja, osa 1/5
Luodaan peruspohja ToDo-client sovellukselle
linkit youtubessa videon kuvauksessa aikaleimoihin
- 00:00 Aloitus, kehityspalvelimen käynnistys
- 03:00 Sovelluksen rautalankamalli
- 22:22 Koodaamisen aloittaminen - TodoList komponentti
- 26:19 Todos-dummy data ja propsit, datan välittäminen alemmalle komponentille.
- 42:02 Komponentin tila käyttäen useState -hookia
- 49:10 Käyttöliittymäkomponentit, components kansio
- 51:29 Export component
- 52:19 Import component
Videolla tehty koodi löytyy myös tästä:
Peruspohja, osa 2/5
Lisätään CreateTodo komponentti, komponenttikohtainen CSS tyylittely sekä toimintallisuuksia ToDo-sovellukseen.
linkit youtubessa videon kuvauksessa aikaleimoihin
- 04:50 Komponenttikohtaisen CSS tyylittelyn import .css tiedostosta
- 30:36 CreateTodo komponentin state
- 45:10 Ehdollinen komponentin renderöinti käyttämällä loogista && operaattoria
- 49:05 Parent komponentin tilan päivittäminen child komponentissa
Komponentit, import & export
2p kohtaan 1. vaatimusmäärittelyssä. (2p/3p)
- Tee omat jsx tiedostot komponenteille TodoList ja TodoItem.
- Siirrä komponenttien koodit luomiisi komponenttikohtaisiin tiedostoihin App.jsx tiedostosta
- Tämän jälkeen varmista että importit ja exportit ovat asianmukaisesti kunnossa ja sovellus toimii kuten aiemmin.
Funktiot rajapintakyselyille
Tämä osio sisältää ohjatun osuuden soveltavat tehtävät (vaatimusmäärittelyn kohdat 1-6).
Rakennetaan rajapintayhteyksille omat yleiskäyttöiset service-funktiot joita voidaan hyödyntää React komponenteissa.
Service funktioiden pääasiallinen tarkoitus on eriyttää http-kyselyihin liittyvä koodi käyttöliittymäkomponenteista. Service funktioita voidaan sitten kutsua esim. komponenttien koodissa seuraavasti:
js
import { createTodo } from "../services/http";
// koodia...
// Lähetettävä data minkä rajapinta olettaa saavansa
// kun tehdään POST request "/todos" endpointtiin.
const newTodo = { title:title, description:description };
// Lähetetään data käyttämällä createTodo funktiota.
// Koska service funktiot ovat aina asynkronisia, käytetään Promisen purkamisessa "then"
// metodia tai async-await menetelmää.
createTodo(newTodo).then(() => {
// Tässä tapauksessa onnistuneen uuden todo-tehtävän luomisen jälkeen
// suljetaan pop-up ikkuna. Taustalla tulee olemaan logiikka joka lataa sulkemisen jälkeen
// todo-tehtävien listauksen uudelleen jotta saadaan ajantasainen data näytettyä listauksessa.
close();
});
// koodia...
Tehtävä
- Tee src kansioon uusi kansio nimeltä services
- Lisää services kansioon uusi tiedosto http.js
- Lisää seuraava koodi luotuun http.js tiedostoon:
js
const TODO_API_BASE_URL = "http://localhost:8000"
// Yleiskäyttöinen rajapintafunktio ToDo-API:lle.
// Mahdollistaa yleisten rajapintaan liittyvien asetusten asettamisen
// kaikille kyselyille jotka tehdään nimenomaiseen rajapintaan.
// Esimerkkinä tietyt headerit jotka rajapinta voisi vaatia kaikilta requesteilta.
async function todoApi(endpoint, options = {}) {
const response = await fetch(TODO_API_BASE_URL + endpoint, options)
// response.ok on true jos http-statuskoodi on pienempi kuin 300, muutoin se on false.
if (!response.ok) throw new Error("Request failed with statuscode: " + response.status)
const data = await response.json()
return data
}
// Tehdään funktiot kaikille rajapinnan avainominaisuuksille.
// Tämän tarkoituksena on pitää rajapintaan liittyvät toiminnallisuudet
// erillään käyttöliittymäkomponenteista. Asynkronisten operaatioiden virheiden
// käsittely toteutetaan muualla koodissa.
// Muuttaa yksinkertaisen javaScript objektin query-stringiksi.
// Palauttaa tyhjän stringin jos params on undefined tai tyhjä objekti {}
function createQueryParams(params) {
if(!params) return ""
return "?" + new URLSearchParams(Object.entries(params)).toString()
}
// Vaatimusmäärittelyn tehtävään 1 (1p/3p) ja tehtävään 2 (3p/4p).
// Toteuta funktioon koodi joka hakee todoApi funktiota
// käyttämällä rajapinnasta listan todo-tehtäviä.
// Funktio ottaa parametrina objektin joka sisältää datan jolla rakennetaan URL:iin query-parametrit.
// params muuttuja voi olla tyhjä objekti {}, {done:true} tai {done:false}.
export async function getTodos(params) {
// Voit hyödyntää tässä createQueryParams funktiota
// Funktio palauttaa listan todo-tehtäviä
}
// Vaatimusmäärittelyn tehtävään 3 (2p/3p).
// Toteuta funktioon koodi joka luo uuden todo-tehtävän käyttämällä todoApi funktiota.
export async function createTodo(newTodo) {
// Funktio palauttaa luodun todo-tehtävän, paluuarvoa ei välttämättä käytetä muualla koodissa.
}
// Vaatimusmäärittelyn tehtävään 4. (2p/3p)
// Toteuta funktioon koodi joka poistaa todo-tehtävän id:n perusteella
// rajapinnasta käyttämällä todoApi funktiota.
export async function removeTodoById(id) {
// Funktiolla ei ole paluu-arvoa
}
// Vaatimusmäärittelyn tehtävään 5 (1p/4p).
// Toteuta funktioon koodi joka hakee todoApi funktiota
// käyttämällä rajapinnasta yksittäisen todo-tehtävän tiedot.
// Todo-tehtävän muokkaamista varten tarvitaan ajantasainen tieto siitä että
// mikä data kyseisellä id:llä olevalla todo-tehtävällä on ennen sen muokkaamista.
export async function getTodoById(id) {
// Palauttaa todo-tehtävän annetulla id:llä
}
// Vaatimusmäärittelyn tehtävään 5. (2p/4p)
// Toteuta funktioon koodi joka muokkaa olemassa olevaa todo-tehtävää
// rajapinnassa käyttämällä todoApi funktiota.
// Tässä tapauksessa rajapinta haluaa id:n myös path parametrina.
// (Voit halutessasi muokata python koodia niin että path parametria ei tarvita vaan id katsotaan
// itse todo-tehtävän datasta.)
export async function updateTodoWithId(id, editedTodo) {
// Palauttaa rajapinnassa muokatun todo-tehtävän
}
// Vaatimusmäärittelyn tehtävään 6. (2p/3p)
// Toteuta funktioon koodi joka päivittää rajapinnassa id:n perusteella yksittäisen
// todo-tehtävän done-statuksen. Done voi olla joko true tai false
export async function toggleDoneWithId(id,currentDone) {
// Palauttaa objektin { done: true } tai { done: false }
}
Vinkki + Esimerkki
Jos Reactin kanssa funktioiden toimivuuden testaaminen tuottaa haasteita, voit käyttää esim. javaScript playgroundia koodin testaamiseen kopioimalla lähtökoodin sinne ja suorittamalla funktiot esim. seuraavasti:
Tehtävän aloitus videolla. Osa 3/5
Käydään videolla läpi mallin vuoksi getTodos ja createTodo servicefunktioden rakentaminen.
Vaatimusmäärittelyn tehtävään 3 täydet pisteet tämän videon pohjalta
- 04:52 React Fragment, tyylittely (index.css)
- 13:20 Rajapintakyselyt Reactissa, useEffect, fetch
- 24:09 Services kansio ja tehtävän tekeminen malliksi
- 35:10 createQueryParams -funktion selitys
- 39:30 Tehtävän 1 & 2 (vaatimusmäärittely) tekemistä.
- 58:50 "createTodo" funktion tekeminen.
- 1:08:30 "createTodo" service funktion käyttöönotto.
- 1:10:40 useEffect dependency lista, todo-tehtävälistauksen päivittäminen uuden lisäämisen jälkeen.
Otetaan käyttöön tehdyt service funktiot React komponenteissa
Osa 4/5
Tehdään vaatimusmäärittelyn tehtävien 2 (1p) ja 6 (1p) React osuus
- 00:00 Aloitus, tehtävän pisteet
- 04:00 Kategoriat: kaikki, tekemättä ja tehdyt. TodoButtons komponentti
- 10:00 TodoButtons komponentin refaktorointi
- 16:05 useEffect funktion suorittaminen valinnan muuttuessa
- 17:20 Todo-tehtävän done-statuksen muuttaminen (vaatimusmäärittelyn 6. kohta)
- 18:40 "toggleDoneWithId" servicefunktion käyttöönotto
- 22:38 Key:n lisääminen buttons listaukseen (fix)
Osa 5/5
Tehdään vaatimusmäärittelyn tehtävät 4 (1p) ja 5 (1p) React osuus
- 01:45 TodoView & Overlay komponentit
- 32:45 "updateTodoWithId" service funktion käyttöönotto
- 45:33 "removeTodoById" service funktion käyttöönotto
Kun ToDo-client on toteutettu tähän saakka ja service funktiot ovat tehty ja toimivat oikein, tehtävä on suoritettu 20p arvoisesti
Videoilla tehty React koodi ilman service funktioita:
Esimerkki soveltavaan tehtävään - Funktiot rajapintakyselyille.
Jos käytät toteuttamaasi lisäominaisuutta ToDo-API:sta, tee tarvittavat funktiot jotka hyödyntävät todoApi funktiota (ks. yläpuolella oleva koodi)
Jos käytät 3. osapuolen rajapintaa clientilta, tee vastaava yleiskäyttöinen rajapintafunktio käyttämällesi rajapinnalle.
Voit tehdä services kansioon esim. seuraavanlaisen jaon eli nimetä http.js -> todos.js ja tehdä uuden tiedoston 3. osapuolen rajapintaa varten (esim. cat_facts.js):
- ./services/todos.js
- ./services/cat_facts.js
Esim.
./services/cat_facts.js
js
const CAT_FACT_API_BASE_URL = "https://cat-fact.herokuapp.com"
async function catFactsApi(endpoint){
const response = await fetch(CAT_FACT_API_BASE_URL + endpoint)
const data = await response.json()
return data
}
export function getCatFacts(){
const facts = await catFactsApi("/facts")
return facts
}
Lisäominaisuus voidaan ottaa käyttöön esim. tekemällä sille kokonaan oman React komponentin seuraavasti:
jsx
import React, { useEffect, useState } from 'react';
import { getCatFacts } from '../services/cat_facts.js';
export function CatFacts() {
const [fact, setFact] = useState();
useEffect(() => {
getCatFacts().then((data) => {
setFact(data[0]);
});
}, []);
if (fact) {
return <div>{fact.text} </div>;
}
return <div>Ladataan...</div>;
}
Otetaan luotu komponentti käyttöön toisessa komponentissa (esim. App komponentti)
jsx
// koodia...
<CatFacts></CatFacts>
// Koodia...