generated at
karaxのループで生成する要素のボタンクリック処理

まえがき
karaxのループで要素を生成したときにボタンクリックイベントで変数を操作しようとしてつまづいて解決した
原因と解決とかを書く

karaxでボタンクリック時に変数を操作するのはよくやる
こんな感じで実装する
karax_test1.nim
import 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.nim
import 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.nim
import 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 という引数にループカウンタを受け取るプロシージャを定義する
受け取った値をプロシージャ内のローカル変数に束縛し、ボタンクリックイベント用のプロシージャを返す
これでボタンクリックイベントが期待通りの動きをするようになった
よかったよかった