Nim and static type noob here. So the problem is when I compile my code and run
it in browser, fill the name and description fields and click add thing button,
my browser console gives following cryptic runtime errors:
TypeError: c_225079 is null quicktest.js:579:16
cstrToNimstr quicktest.js:579
HEX24_10841036 quicktest.js:2878
HEX3Aanonymous_10850242 quicktest.js:3172
wrapper_10750162 quicktest.js:3049
HEX3Aanonymous_10190109 quicktest.js:1234
Run
I cannot interpret the error given, it seems that the error has something to do
with the cstring -> string transformation. I've isolated the problem somewhat
to the handler proc actionAddThing and the getVNodeById karax procedure. When I
substitute the name and desc values into something more static eg. "foo" and
"bar" the script works. (well of course I cannot capture user inputs but
anyways...)
Here is the rather verbose code (sorry it's already pretty stripped version of
the original code) with some commenting of my intents:
include karax/prelude
# These are used in the html text input field's id:
const
IdInputThingName: kstring = "inputThingName"
IdInputThingDesc: kstring = "inputThingDesc"
type
Thing = ref object
uid: kstring
name: kstring
desc: kstring
proc `$`(t: Thing): string =
return "Thing(uid=" & $t.uid & ", name=" & $t.name & ", desc=" &
$t.desc & ")"
# Some getters for thing, after creating the thing, it's attributes
shouldn't
# be edited.
proc getName*(thing: Thing): kstring =
return thing.name
proc getUid*(thing: Thing): kstring =
return thing.uid
proc getDesc*(thing: Thing): kstring =
return thing.desc
proc newThing(uid, name, desc: kstring): Thing =
result = Thing(uid: uid, name: name, desc: desc)
# proc which returns the thing factory with the id initialised to zero.
# the returned closure factory will increment the id everytime the thing
# is created and passes the uid to Thing constructor.
proc thingBuilder(): proc(name, desc: kstring): Thing =
var nextId = 0
return proc (name, desc: kstring): Thing =
nextId.inc()
result = newThing("uid-" & $nextId, name, desc)
# Creating the global factory:
let thingFactory = thingBuilder()
# App state and related procs/methods
type
AppState = ref object
things: seq[Thing]
proc newAppState(): AppState =
# Default init is empty things list:
result = AppState(things: @[])
# Should modify the existing app state and it's
# things list by adding the given thing into the list.
proc addThing*(self: var AppState, t: Thing) =
self.things.add(t)
# Getter for the things list in app state.
# Not sure if it's making copies or not?
proc getAllThings*(self: AppState): seq[Thing] =
result = @[]
for t in self.things:
result.add(t)
# Handler for clicking the button. Should read the
# text input field's (created in the makeControls proc) texts and create
# the thing using the thingFactory. After this it should add the created
# thing into the app state's thing list.
proc actionAddThing(state: var AppState): proc() =
return proc() =
# These doesn't work, why?!
let name = getVNodeById(IdInputThingName).value()
let desc = getVNodeById(IdInputThingDesc).value()
# If I substitute name and desc with "foo" and "bar"
# this works. WTF!? Why I cannot get the values in those nodes?
# let name = kstring("foo")
# let desc = kstring("bar")
# Create the thing based on the given name and desc:
let thing = thingFactory(name, desc)
echo("Thing created: ", $thing)
# Add the created thing into app state's thing list:
state.addThing(thing)
echo $state.getAllThings()
# Supposed to return odd string if the given number is
# odd and even if even...duh. String is used when making
# things and added into their class attribute.
proc evenOdd(idx: int): kstring =
if idx %% 2 != 0:
return "even"
return "odd"
# Make/render the thing list based on the given sequence:
proc makeThingList(tlist: seq[Thing]): VNode =
result = buildHtml(tdiv(class="thing-list")):
for idx, th in tlist:
tdiv(id=th.uid, class="thing " & evenOdd(idx)):
text kstring($th)
# Make/render the controls button and the text inputs
# necessary to create new thingies:
proc makeControls(state: var AppState): VNode =
result = buildHtml(tdiv(class="controls")):
label(`for`=IdInputThingName):
text "Thing Name: "
input(`type`="text", id=IdInputThingName,
placeholder=kstring("Input the thing name here..."))
label(`for`=IdInputThingDesc):
text "Thing Description: "
input(`type`="text", id=IdInputThingDesc,
placeholder=kstring("Input the thing desc here..."))
button(onclick=actionAddThing(state)):
text "Add Thing"
# Make/render the whole dom (return closure for karax):
proc createDom(state: var AppState): proc(): VNode =
return proc(): VNode =
result = buildHtml(tdiv(id="things-are-dope")):
makeThingList(state.getAllThings())
makeControls(state)
when isMainModule:
# Initialize app state as variable because I want to change it's
insides later
# (this is probably a bad idea...)
var state = newAppState()
# Add few things into the app state's list of things:
state.addThing(thingFactory("Thing A", "Desc about Thing A"))
state.addThing(thingFactory("Thing B", "Desc about Thing B"))
state.addThing(thingFactory("Thing C", "Desc about Thing C"))
# Now we are ready to create the DOM:
setRenderer createDom(state)
Run
I'm hopeful that this isn't again in the series of stupid questions... :)