ClojureScript& Om:编写document.hash的最佳实践

时间:2015-01-28 22:00:27

标签: web-applications routing clojurescript om

我有一个"你好,世界!"使用Om的ClojureScript中的应用程序(从" Chestnut" lein模板生成)。

目标是让它设置为:

  • document.location.hash值反映了对(:route app-state)向量的更改。
  • (:route app-state)向量反映了对document.location.hash值的更改。
  • (:route app-state)更改时,应用重新呈现。

请注意,我打算将(:route app-state)向量作为应用程序关于应用程序当前状态的唯一事实来源。改变它的一种机制是用户修改URL。

我应该在何处以及如何将此行为附加到Om?


这是我的"你好,世界!"应用

(ns demo.core
  (:require [om.core :as om :include-macros true]
            [om.dom :as dom :include-macros true]
            [clojure.string :as string]))

(defonce app-state (atom {:text "Hello, World!"
                          :route ["some" "app" "route"]}))

(defn update-location-hash [app owner]
  (reify
    om/IRender
    (render [_]
      (set! js/window.location.hash
            (string/join "/" (flatten ["#" (:route app)])))
            (dom/div nil ""))))

(om.core/root
  update-location-hash
  app-state
  {:target (. js/document (getElementById "app"))})


(defn main []
  (om/root
    (fn [app owner]
      (reify
        om/IRender
        (render [_]
          (dom/h1 nil (:text app)))))
    app-state
    {:target (. js/document (getElementById "app"))}))

这样就可以在页面加载时成功写入document.hash。最终,这将是一个单页应用程序,它使用哈希导航来进行视图更改。

由于必须在(render ) update-location-hash函数中返回一个除了满足{{功能。

1 个答案:

答案 0 :(得分:0)

好的,我想出了我需要的东西。它对我来说似乎很干净而且很有效。 为了完整起见,我只需要添加一个页面加载类型的侦听器来检查哈希值和渲染器,它将状态更改呈现给哈希值。这应该是非常简单的(基于我之前的代码)。

(ns demo.core
  (:require-macros [cljs.core.async.macros :refer [go]])
  (:require [goog.events :as events]
            [goog.events.EventType :as EventType]
            [cljs.core.async :as async :refer [>! <! put! chan]]
            [om.core :as om :include-macros true]
            [om.dom :as dom :include-macros true]
            [clojure.string :as string]))

;; Not sure what this does.
(enable-console-print!)

(defn listen
  "An event listener factory.  Given an element and an event type, return a
  channel that can be polled for event outputs."
  [el type]
  (let [out (chan)] (events/listen el type #(put! out %)) out))

(defonce app-state
  (atom {:text "Hello, World!"
         :mouse [0 0]
         :route ["some" "app" "route"]}))

(defn url-to-route
  "Given a url, parse the hash value as an app route.  In general, the
  resultant route has these properties:

  * route components are split on a solidus
  * empty components are ignored (nill or all-whitespace)

  A route like this:

    ['foo' 'bar' 'baz']

  will be produced by all of the following urls (and others):

    http://my-app.com/#/foo/bar/baz
    http://my-app.com/#foo/bar/baz
    http://my-app.com/#/foo// /bar//baz
    http://my-app.com/#/ / / /foo/bar////baz"
  [url]
  ;; Split the url at a hash followed by zero or more slashes and
  ;; whitespace.  Then take anything after the hash and split it on one or
  ;; more slashes (ignoring whitespace).
  (string/split (second (string/split url #"#[/\s]{0,}" 2)) #"[/\s]+"))

(defn layout
  "The central application layout component.  This registers global event
  listeners and renders the application's root DOM nodes."
  [app owner]
  (reify
    om/IWillMount
    (will-mount [_]
      ;; Handle various events.  When an event is triggered, format the
      ;; response.
      (let [;; Listen for changes to the mouse position
            mouse-chan (async/map
                         (fn [event] [(.-clientX event) (.-clientY event)])
                         [(listen js/window EventType/MOUSEMOVE)])

            ;; Listen for changes to the URL's hash
            hash-chan (async/map
                        (fn [event] (url-to-route (-> event .-event_ .-newURL)))
                        [(listen js/window EventType/HASHCHANGE)])]
        ;; Watch the stream and update the application state whenever
        ;; anything changes.
        (do
          (go (while true (om/update! app :route (<! hash-chan))))
          (go (while true (om/update! app :mouse (<! mouse-chan)))))))

    om/IRender
    (render [_]
      (dom/div
        nil
        (dom/div nil (when-let [route (:route app)] (pr-str (:route app))))
        (dom/div nil (when-let [pos (:mouse app)] (pr-str (:mouse app))))
        (dom/h1 nil (:text app))))))

(defn main []
  (om/root
    layout
    app-state
    {:target (. js/document (getElementById "app"))}))