11 minute read

2.3 Lage interaktive komponenter

Next Article
Sammendrag

Sammendrag

Slik ser src/routes ut når vi jobber med handlelisten.

Slik ser telleren ut hvis vi prøver koden uten noe annet på en nettside.

I dette delkapittelet skal vi utvikle en nettside med en handleliste. Her skal vi bruke en interaktiv komponent som brukerne kan klikke på og endre. Komponenten heter Teller og brukes for å holde styr på antall varer i handlelisten.

Nedenfor ser du koden for Teller-komponenten slik den ser ut i utgangspunktet. Telleren har en verdi, og brukeren kan øke og minke denne ved å klikke på pluss- og minusknappene ved siden av verdien. Koden ligger lagret i filen Teller.svelte, som ligger i mappen src/routes/_komponenter.

1 <!-- _komponenter/Teller.svelte --> 2 <script> 3 let verdi = 0 //Antallet av en vare 4 </script>

5 6 <span> 7 <button on:click={() => (verdi = verdi – 1)}>-</button> 8 {verdi} 9 <button on:click={() => (verdi = verdi + 1)}>+</button> 10 </span>

Vi har lyst til å bruke telleren i handleliste.svelte. Vi skriver en import-setning som peker på filen Teller.svelte. For å sette telleren inn som en komponent i filen skriver vi <Teller /> der vi vil at komponenten skal være plassert.

1 <!-- handleliste.svelte --> 2 <script> 3 import Teller from "./_komponenter/Teller.svelte" 4 </script>

5 6 <h1>Handleliste</h1> 7 <ul> 8 <li>Løk: <Teller /></li> 9 <li>Epler: <Teller /></li> 10 <li>Melk: <Teller /></li> 11 </ul>

SKRIVESTILENE KEBAB-CASE, PASCALCASE OG CAMELCASE

Når vi vil at en Svelte-fil skal representere en nettside som brukeren kan navigere til, navngir vi den med såkalt kebab-case: smaa-bokstaver-med-bindestreker.svelte. Det er fordi kebab-case følger den anbefalte skrivemåten for nettadresser, slik at brukeren skal slippe å skille mellom små og store bokstaver. Filen med navnet handleliste. svelte blir for eksempel gjort om til en nettadresse som slutter med /handleliste.

Navnet på komponenter vi importerer, kan ikke inneholde bindestreker. Derfor skriver vi navnet på slike komponenter i PascalCase. I PascalCase kutter vi ut alle mellomrom og gjør den første bokstaven i hvert ord stor – også i det første ordet. Vi vil at filen vi importerer fra, skal ha samme navn som komponenten vi importerer. Derfor navngir vi filer som skal brukes som komponenter (og ikke som selvstendige sider), med PascalCase: Teller.svelte.

Vi bruker camelCase når vi navngir variabler og funksjoner:

navnetTilVariabelEllerFunksjon

Vi bruker PascalCase når vi navngir komponenter:

Komponentnavn

Vi bruker kebab-case i navnet til filer som skal gjøres om til nettsider:

navnet-til-filen.svelte

EKSTRASTOFF

Navnet på de ulike skrivestilene og deres opprinnelse

kebab-case har fått navnet sitt fordi teksten ser ut som matbiter som er stukket på et grillspyd. Grillspyd kalles «shish kebab» i mange land. PascalCase har fått navnet sitt fra programmeringsspråket Pascal, som igjen er oppkalt etter matematikeren Blaise Pascal.

camelCase har fått navnet sitt fordi de store bokstavene i teksten kan se ut som puklene på en kamel.

SCREAMING_SNAKE_CASE er en stil vi bruker for variabler som vi eksporterer. Denne stilen har fått navnet sitt fordi de store bokstavene får den til å se ut som en ropende variant av skrivestilen snake_case. snake_case er som kebab-case, men med understrekingstegn i stedet for bindestreker.

DEFINERE KOMPONENT-EGENSKAPER

Når vi jobber med HTML-elementer, kan vi bruke attributter for å endre måten de oppfører seg og ser ut på. Hvis vi for eksempel skriver <input type="number" min={0} />, blir input-feltet et tallfelt som ikke kan ha lavere verdi enn 0. Vi kan gjøre noe lignende i Svelte-komponenter. Da kaller vi det en egenskap (property på engelsk).

Verdier vi angir i et HTML-element, kalles attributter. Verdier vi angir i en komponent, kalles egenskaper.

I stedet for å skrive <li>Løk: <Teller /></li>, slik vi gjorde i handlelisten tidligere, kan vi for eksempel skrive <li><Teller navn="Løk" /></li>. Da viser telleren navnet på varen med et kolon etterpå. Dette er en fordel hvis vi senere vil endre måten varene vises på, for da kan vi endre koden én gang i Teller.svelte i stedet for tre ganger i handleliste.svelte. Vi kan gjøre en hvilken som helst variabel i en Svelte-komponent til en egenskap ved å skrive export foran den.

1 <!-- _komponenter/Teller.svelte --> 2 <script> 3 export let navn 4 let verdi = 0 5 </script>

6

7 <span> 8 {navn}: 9 <button on:click={() => (verdi = verdi – 1)}>-</button> 10 {verdi} 11 <button on:click={() => (verdi = verdi + 1)}>+</button> 12 </span>

Handlelisten ser ut akkurat slik som den gjorde før.

Lage komponent-egenskaper

Egenskaper er verdier som sendes inn til en komponent utenfra, via komponentens tagg. For å si at vi vil ha en verdi utenfra i en Svelte-komponent, skriver vi en linje

som begynner med export let egenskapensNavn. For å angi en verdi for en egenskap skriver vi

<Komponenten egenskapensNavn={verdien} />.

I handleliste.svelte angir vi det som tidligere sto foran Teller-taggen, som verdien til egenskapen navn.

1 <!-- handleliste.svelte --> 2 <script> 3 import Teller from "./_komponenter/Teller.svelte" 4 </script>

5 6 <h1>Handleliste</h1> 7 <ul> 8 <li><Teller navn="Løk" /></li> 9 <li><Teller navn="Epler" /></li> 10 <li><Teller navn="Melk" /></li> 11 </ul>

EGENSKAPER MED STANDARDVERDI

Av og til ønsker vi at det skal være mulig å slippe å oppgi verdien for en egenskap. I HTML-attributter kan vi for eksempel la være å oppgi verdien for attributtet type i et input-felt, og da får det automatisk verdien "text". Det er med andre ord likegyldig om vi skriver <input /> eller <input type="text" />. Det er også mulig å gi egenskapene i Svelte-komponenter slike standardverdier.

For å angi en standardverdi for en egenskap trenger vi bare å skrive et likhetstegn fulgt av standardverdien etter at vi har definert egenskapen, altså export let

egenskapensNavn = standardverdi.

Vi vil at brukeren skal kunne oppgi et antall for Teller, og vi vil at standardverdien skal være 0. For å få til dette i Teller-komponenten trenger vi bare å sette inn export foran let verdi = 0. Vi hadde allerede bestemt at verdi skulle starte med verdien 0, men nå tillater vi altså at brukeren kan overstyre denne verdien.

1 <!-- _komponenter/Teller.svelte --> 2 <script> 3 export let navn 4 // Vi skriver "export" foran og "= 0" etter "let verdi". 5 export let verdi = 0 6 </script> 7 <!-- HTML-delen er lik som før. -->

Løk får antallet 0, slik standardverdien tilsier. For å angi startverdier skriver vi verdi={ startverdi } i Teller-taggen for varene i Handleliste.svelte. For å teste at standardverdien fungerer som den skal, lar vi være å sende en startverdi til løk-telleren.

1 <!-- handleliste.svelte --> 2 <script> 3 import Teller from "./_komponenter/Teller.svelte" 4 </script>

5 6 <h1>Handleliste</h1> 7 <ul> 8 <li><Teller navn="Løk" /></li> 9 <li><Teller navn="Epler" verdi={5} /></li> 10 <li><Teller navn="Melk" verdi={3} /></li> 11 </ul>

Når vi definerer en egenskap, angir vi en standardverdi ved å skrive koden

export let egenskapensNavn = standardverdi Vi angir egenskapens verdi i taggen til en komponent, slik:

<Komponent egenskapensNavn={verdi} />

BINDE KOMPONENTERS EGENSKAPER TIL VARIABLER

I Svelte kan vi binde HTML-elementenes attributter til verdien av variabler. Kodesnutten nedenfor gjør for eksempel at variabelen verdi får samme verdi som en bruker skriver inn i input-feltet.

1 <!-- input-binding.svelte --> 2 <script> 3 let verdi = "" 4 </script> 5 <label>Verdi: <input bind:value={verdi} /></label> 6 <div>Du skrev {verdi}</div>

Det er mulig å binde Svelte-komponentenes egenskaper til verdien av variabler på samme måte. Hvis vi vil binde de ulike varene i handlelisten til en variabel, kan vi skrive bind:verdi={variabelnavn} for hver enkelt vare. Koden i Teller.svelte trenger vi ikke å endre i det hele tatt.

1 <!-- handleliste.svelte --> 2 <script> 3 import Teller from "./_komponenter/Teller.svelte"

4 5 let antallLoek 6 let antallEpler = 5 7 let antallMelk = 3 8 </script>

9 10 <h1>Handleliste</h1> 11 <ul> 12 <li><Teller navn="Løk" bind:verdi={antallLoek} /></li> 13 <li><Teller navn="Epler" bind:verdi={antallEpler} /></li> 14 <li><Teller navn="Melk" bind:verdi={antallMelk} /></li> 15 </ul>

16 17 <p>Du har {antallLoek + antallEpler + antallMelk} varer i handlelisten.</p>

Fordi vi ikke har gitt antallLoek noen verdi, får denne variabelen standardverdien 0 når vi binder den. Du kan prøve å endre standardverdien til 1 i Teller.svelte. Da skal du få opp en melding om at du har 9 varer i handlelisten i stedet for 8, og verdien for løk vil være 1.

Når en egenskap verken har en standardverdi eller får en verdi i komponentens tagg, får den verdien undefined. Hvis vi bare skriver <Teller /> og glemmer å sende inn en verdi for navnet, ser det altså ut som på bildet til venstre:

LAGRE VARENE I ET ARRAY OG VISE DEM MED EN EACH-BLOKK

I kapittel 1 lærte du å iterere over et array med en each-blokk. I handlelisten vi holder på å utvikle, er det en fordel at varene er lagret i et array. Da kan brukeren legge til så mange varer hun vil, og ikke et forhåndsbestemt antall. Vi venter litt med å legge til funksjonalitet som gjør det mulig for brukeren å legge til flere varer, og begynner med å flytte det vi allerede har, over i et array.

1 <!-- handleliste.svelte --> 2 <script> 3 import Teller from "./_komponenter/Teller.svelte"

4

5 let varer = [ 6 { navn: "Løk", antall: 0 }, 7 { navn: "Epler", antall: 5 }, 8 { navn: "Melk", antall: 3 },

9 ] 10 11 const summerAntall = (varer) => { 12 let sum = 0 13 for (let vare of varer) {

14 sum = sum + vare.antall

15 } 16 return sum

17 } 18 19 $: totaltAntall = summerAntall(varer) 20 </script>

21 22 <h1>Handleliste</h1> 23 <ul> 24 {#each varer as vare} 25 <li><Teller navn={vare.navn} bind:verdi={vare.antall} /></li> 26 {/each} 27 </ul>

28 29 <p>Du har {totaltAntall} varer i handlelisten.</p>

Det første vi gjorde, var å legge varene til som objekter i arrayet varer. Siden arrayet kan inneholde mange verdier, er vi nødt til å bruke en løkke for å regne ut summen. Derfor har vi laget funksjonen summerAntall, som tar inn antall varer som input og bruker en løkke til å summere alle varene.

Linjen $: totaltAntall = summerAntall(varer) gjør at verdien til variabelen totaltAntall regnes ut på nytt hver gang brukeren endrer verdien til varer

Etter at vi har klikket på knappen tre ganger, ser det slik ut. ved å klikke på pluss- og minusknappene. Du husker kanskje fra tidligere at $: markerer at linjen skal reagere på alle endringer i stedet for å bare kjøres når vi åpner en nettside. Hvis vi hadde startet linjen med let i stedet for $:, hadde programmet bare regnet ut verdien når nettsiden åpnes, og ikke oppdatert den ved senere endringer.

I HTML-delen av dokumentet har vi skrevet en each-blokk som oppretter en Teller-komponent for hver verdi i varer-arrayet. Legg merke til at vi i for-løkken skrev let vare of varer. Da vi at en verdi i arrayet midlertidig skal få navnet vare inni denne iterasjonen av blokken, på samme måte som i en each-blokk.

LEGGE TIL VARER I HANDLELISTEN

For å gjøre handlelisten litt mer spennende vil vi gi brukerne muligheten til å legge til varer. Vi velger å lage en knapp og definere en funksjon som legger til en vare i listen. Nå som brukerne kan legge til varer selv, fjerner vi de tre varene fra handlelisten og lar varer være et tomt array.

1 <!-- handleliste.svelte --> 2 <script> 3 // Likt før dette, men varer er et tomt array når vi starter. 4 let varer = [] 5 // Resten av script-delen er lik. 6 </script>

7 8 <h1>Handleliste</h1> 9 <ul> 10 {#each varer as vare} 11 <li><Teller navn={vare.navn} bind:verdi={vare.antall} /></li> 12 {/each} 13 <li> 14 <button

15 16 17 on:click={() => { varer = [...varer, { navn: "Ny vare", antall: 0 }]

18 > 19 Legg til vare 20 </button> 21 </li> 22 </ul>

23 24 <p>Du har {totaltAntall} varer i handlelisten.</p>

ENDRE NAVN PÅ VARER

Teller har en åpenbar mangel: Når brukerne legger til nye varer, ønsker de å sette egne navn på dem. Kanskje vil de også redigere navnet på varer de allerede har lagt til.

Vi bør legge til en knapp i Teller.svelte som gjør at brukerne kan endre navnet på telleren. Når brukerne trykker på denne knappen, bør hele telleren bli til et input-felt, slik at de bare har muligheten til å redigere navnet, ikke antallet. Brukerne bør få to knapper å trykke på: én for å lagre resultatet og en annen for å avbryte og gå tilbake til navnet slik det var før de redigerte det. Dette løser vi i koden på neste side.

1 <!-- _komponenter/Teller.svelte --> 2 <script> 3 export let navn 4 export let verdi = 0

5 6 // Variabler for å styre endringen 7 let endrerNavn = false 8 let endringsforslag = "" 9 </script>

10 11 {#if endrerNavn} 12 <!-- Dette vises hvis man trykker på Endre-knappen. --> 13 <label>Nytt navn: <input bind:value={endringsforslag} /></label> 14 <button 15 on:click={() => {

16 17 navn = endringsforslag endrerNavn = false

18 }} 19 > 20 Lagre 21 </button> 22 <button on:click={() => (endrerNavn = false)}> Avbryt </button> 23 {:else} 24 <span> 25 <!--

26 27 Vi har lagt navn i en span-tagg med litt rom til høyre og kuttet ut kolonet.

28 --> 29 <span style="margin-right: 0.5rem">

30 {navn}

31 32 <!-- Knapp for å aktivere endring --> <button

33 34 35 36 37 > on:click={() => { endringsforslag = navn endrerNavn = true

38



39 </button> 40 </span> 41 <button on:click={() => (verdi = verdi - 1)}>–</button> 42 {verdi} 43 <button on:click={() => (verdi = verdi + 1)}>+</button> 44 </span> 45 {/if}

Merk: Når du prøver ut denne handlelisten, oppdager du kanskje at den oppfører seg litt rart i noen situasjoner. Dette er en feil vi har beholdt med vilje, slik at vi kan se på den i neste delkapittel.

This article is from: