karaxのループで生成する要素のボタンクリック処理
まえがき
karaxのループで要素を生成したときにボタンクリックイベントで変数を操作しようとしてつまづいて解決した
原因と解決とかを書く
karaxでボタンクリック時に変数を操作するのはよくやる
こんな感じで実装する
karax_test1.nimimport karax / [kbase, vdom, kdom, vstyles, karax, karaxdsl, jdict, jstrutils, jjson, kajax]
var
text = cstring"not clicked"
proc createDom(): VNode =
result = buildHtml(tdiv):
text text
button:
text "click me"
proc onclick(ev: Event, n: VNode) =
echo "clicked"
text = cstring"clicked"
setRenderer createDom
ボタンをクリックすると text
変数が更新されて画面の描画が更新される
以下の画面が描画される
簡単
ループで要素を生成したときにへんてこなことになる
こんな感じの実装
karax_test2.nimimport karax / [kbase, vdom, kdom, vstyles, karax, karaxdsl, jdict, jstrutils, jjson, kajax]
var
text = cstring"not clicked"
texts = [cstring"not clicked", cstring"not clicked", cstring"not clicked", cstring"not clicked"]
proc createDom(): VNode =
result = buildHtml(tdiv):
text text
button:
text "click me"
proc onclick(ev: Event, n: VNode) =
echo "clicked"
text = cstring"clicked"
hr()
for i, _ in texts:
text texts[i]
button:
text "click me"
proc onclick(ev: Event, n: VNode) =
echo "clicked"
texts[i] = cstring"clicked"
setRenderer createDom
texts
でループして、ループカウンタで texts
にインデックスアクセスして上書きする
何も違和感ない実装のように見える
が、これだと問題がある
上のソースをビルドすると以下の画面ができる
横線の下部分がループで生成している
問題なさそう
この状態で横線下の一番左のボタンをクリックすると
一番右のテキストが更新されている
???
解消法
どういうわけか、 proc onclick
でクリックイベントを定義してプロシージャ内でループカウンタを使うと、一番最後のループカウンタの値がセットされているらしい
回避策として、 onclick
用のプロシージャを生成するプロシージャを定義する必要がある
以下のように実装する
karax_test3.nimimport karax / [kbase, vdom, kdom, vstyles, karax, karaxdsl, jdict, jstrutils, jjson, kajax]
var
text = cstring"not clicked"
texts = [cstring"not clicked", cstring"not clicked", cstring"not clicked", cstring"not clicked"]
# ↓ここが大事
proc buttonOnClicked(i: int): proc(ev: Event, n: VNode) =
let i = i
result = proc(ev: Event, n: VNode) =
echo "clicked"
texts[i] = cstring"clicked"
proc createDom(): VNode =
result = buildHtml(tdiv):
text text
button:
text "click me"
proc onclick(ev: Event, n: VNode) =
echo "clicked"
text = cstring"clicked"
hr()
for i, _ in texts:
text texts[i]
button(onclick = buttonOnClicked(i)):
text "click me"
setRenderer createDom
buttonOnClick
という引数にループカウンタを受け取るプロシージャを定義する
受け取った値をプロシージャ内のローカル変数に束縛し、ボタンクリックイベント用のプロシージャを返す
これでボタンクリックイベントが期待通りの動きをするようになった
よかったよかった