descjop で遊ぼう day 10 : Om basedテンプレートにボタンをつけるその2
続けてhelloworld_om_based
プロジェクトにボタンをつけて動的な処理をつけていきます。
前回の続きなので、今から読んだ方は前回のところまで作業を終えておいてください。
さて、ボタンを押すと、JavaScriptのアラートが出たのですが、まだそれだけです。
続いて、ボタンを押したら見出しが変わるようにしてみましょうか。
(js/alert "pressed!")
は、一旦コメントアウトして、(om/transact! state :message (fn [] "World."))
を追加します。
追加しおえたコードは、下記のとおりとなります。
(defn mount-root []
(om/root
(fn [state owner]
(reify om/IRender
(render [_]
(dom/div nil
(dom/h1 nil (:message state))
(dom/button #js {:onClick (fn [e]
;; (js/alert "pressed!")
(om/transact! state :message (fn [] "World.")))
:className "press"}
"Hello")))))
app-state
{:target (. js/document
(getElementById "app"))}))
transact!
というのは、ざっくりと言うと、アプリケーションの状態を書き換える関数で、
(defn transact!
([cursor f] ...)
([cursor korks f] ...)
([cursor korks f tag]) ...)
こんな風に利用できることが、APIマニュアルに書かれています。
今回の使用例でいうと、
(om/transact! カーソル マップのキーワード 実行される関数)
という形で書いています。
ここで、カーソルという概念が出てきました。カーソルというのは、アプリケーション全体の状態のうち、コンポーネントが管理するべきアプリケーションの状態の一部です。
カーソルが活躍するのはコンポーネントを使ってアプリケーションを構築しはじめたときです。
現時点では、アプリケーションの状態くらいにとらえておいてよいでしょう。
Omでは、アプリケーションを作成する際に、アプリケーションの状態を管理するマップを用意して、それを渡すという状態の管理方法を使っています。
ここで、コード全体を見渡してみます。
(defonce app-state (atom {:message "Hello om world!"}))
(defn mount-root []
(om/root
(fn [state owner]
(reify om/IRender
(render [_]
(dom/div nil
(dom/h1 nil (:message state))
(dom/button #js {:onClick (fn [e]
;; (js/alert "pressed!")
(om/transact! state :message (fn [] "World.")))
:className "press"}
"Hello")))))
app-state
{:target (. js/document
(getElementById "app"))}))
(defn init! []
(mount-root))
まず、(defonce app-state (atom {:message "Hello om world!"}))
というところで、最初の状態を定義しています。
app-state
がアプリケーションの状態を指しているのです。
mount-root
関数は今まで取り上げてきた通りですが、内容としてはom/root
を実行した結果を返します。
om/root
は、Omアプリケーションを作る際に必要なアプリケーションのルートで、実際にはコンポーネントなどを用意してそれらを組み合わせることでOmアプリケーションとして機能させます。
今回は直接OmアプリケーションのRoot内でいろいろやっていますが。
root関数は、こんな風に定義されており、
(defn root
([f value options] ...))
今回は、app-state
を第2引数に渡しています。
app-state
は、IRender
かIRenderState
のインスタンスを返す関数として定義されるf
に対して、ここではstate
という名前で渡ってくるように定義しています。
それによって、(:message state)
は、ここでは(:message @app-state)
と同様の結果が得られるのです。
Omでは、アプリケーションの状態の変化に応じて、仮想DOMを自動で更新することができます。
例えば下記のようにコードを書き換えてみましょう。
(defn mount-root []
(om/root
(fn [state owner]
(reify om/IRender
(render [_]
(dom/div nil
(dom/h1 nil (:message state))
(dom/p {:className "my-message"} (:message state))
(dom/button #js {:onClick (fn [e]
;; (js/alert "pressed!")
(om/transact! state :message (fn [] "World.")))
:className "press"}
"Hello")))))
app-state
{:target (. js/document
(getElementById "app"))}))
h1の下にpタグを出力する(dom/p {:className "my-message"} (:message state))
を置きました。
実行すると、ボタンを押した際にh1要素とp要素両方が変わっているのがわかるはずです。
Colophon
- 編集長
- Greative GK. 原一浩 ( kara_d )
- 製版システム
- Clojure / Compojure / Ring / Enlive / markdown-clj / Jetty / MySQL
- Share this magazine!
- Follow designudge
- Follow @designudge