Nim per il frontend web: componenti Karax
Scritto da Andrea Manzini
Abbiamo visto quanto sia semplice con Karax creare delle piccole applicazioni interattive, senza scrivere una riga di Javascript. Nella realtà, la programmazione web è molto più complessa ma possiamo semplificare il processo suddividendo le pagine in componenti indipendenti, che poi verranno assemblati ed “usati” nella pagina principale.
In questa guida vedremo dunque un semplice componente che modella un “orologio” autonomo e configurabile dall’esterno, insieme ad una semplice applicazione che sfrutta il componente.
L’idea di fondo è che ogni componente è un oggetto Nim, derivato da una specifica classe VComponent (fornita da karax).
sul repository potete trovare il sorgente completo dell’applicazione e una live demo funzionante, ma evidenziamo anche qui le parti salienti. Partiamo dal componente:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| type
KClock* = ref object of VComponent
currentTime: DateTime
offset: TimeInterval
timer: TimeOut
prefix: string
# return a VNode with the html rendered for the component
proc render(c: VComponent): VNode =
let self = KClock(c)
buildHtml(tdiv):
let value = format(self.currentTime+self.offset, "HH:mm:ss")
p:
text "Local Time " & self.prefix & " => " & value
# update the clock value and re-triggers a timer
proc update(self: KClock) =
self.currentTime = now()
self.timer = setTimeout( () => self.update, 100)
markDirty(self) # need to be re-rendered
redraw()
# create, initialize and return a new Clock object
proc new*(T: type KClock, tzoffset = 0): KClock =
let self = newComponent(KClock, render)
self.currentTime = now()
self.offset = initTimeInterval(hours = tzoffset)
self.timer = setTimeout(() => self.update, 100)
self.prefix = if tzoffset >= 0: "+" else: ""
self.prefix.add $tzoffset
return self
|
Questo oggetto avrà almeno:
- alcuni campi per mantenere il suo stato interno
- un metodo render che ritorna la sua rappresentazione HTML
- un costruttore, per inizializzare gli attributi e creare l’istanza dell’oggetto
in più, nel nostro caso, avremo un metodo update (righe 19-23) che aggiorna periodicamente lo stato dell’oggetto e si preoccupa di informare Karax che questo nodo del Virtual Dom va ridisegnato. La funzione setTimeout
non fa parte di Karax ma è inclusa nella libreria standard, package std/dom
.
Una volta salvato il componente in un file appropriato, non ci resta che “usarlo” in una applicazione web. Per andare un po’ oltre i semplici esempi dell’introduzione, includiamo la possibilità di creare e distruggere dinamicamente gli “orologi” tramite pulsanti e di configurare il fuso orario con una casella di input numerico. Nonostante queste funzionalità, l’applicazione rimane leggibile perchè la logica della gestione componenti è incapsulata nella classe separata.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| var
clocks: seq[KClock] # keep a list of clocks
offset: kstring # value entered in the input box
proc render(): VNode =
buildHtml(tdiv):
h2:
label(`for` = "offset"):
text "Please enter Timezone offset (-12 .. +12)"
input(type = "number", id = "offset"):
proc oninput(ev: Event; n: VNode) =
offset = n.value
button:
text "Add a new Clock"
proc onclick(ev: Event; n: VNode) =
let tzofs = parseInt(offset)
if tzofs >= -12 and tzofs <= 12:
clocks.add(KClock.new(tzofs))
button:
text "Remove last Clock"
proc onclick(ev: Event; n: VNode) =
discard clocks.pop()
for clock in clocks:
h1:
clock
setRenderer render
|