1

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

目标是使其设置为:

  • 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 )这对我来说感觉很脏,因为必须在函数中返回一个 DOM 元素,update-location-hash除了满足函数的要求之外没有任何目的render

4

1 回答 1

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"))}))
于 2015-01-29T17:20:27.350 回答