Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
om-async intermediate tutorial - add class functionality not working
core.cljs
---------
(ns om-async.core
(:require-macros [cljs.core.async.macros :refer [go]])
(:require [cljs.core.async :as async :refer [put! chan alts!]]
[goog.dom :as gdom]
[om.core :as om :include-macros true]
[om.dom :as dom :include-macros true]
[om-sync.core :refer [om-sync]]
[om-sync.util :refer [tx-tag edn-xhr]]))
(enable-console-print!)
(def ^:private meths
{:get "GET"
:put "PUT"
:post "POST"
:delete "DELETE"})
(def app-state
(atom {:classes []}))
(defn display [show]
(if show
#js {}
#js {:display "none"}))
(defn handle-change [e data edit-key owner]
(om/transact! data edit-key (fn [_] (.. e -target -value))))
(defn end-edit [data edit-key text owner cb]
(om/set-state! owner :editing false)
(om/transact! data edit-key (fn [_] text) :update)
(when cb
(cb text)))
(defn editable [data owner {:keys [edit-key] :as opts}]
(reify
om/IInitState
(init-state [_]
{:editing false})
om/IRenderState
(render-state [_ {:keys [editing on-edit]}]
(let [text (get data edit-key)]
(dom/tr nil
(dom/td nil
(dom/span #js {:style (display (not editing))} text)
(dom/input
#js {:style (display editing)
:value text
:size "60"
:onChange #(handle-change % data edit-key owner)
:onKeyPress #(when (== (.-keyCode %) 13)
(end-edit data edit-key text owner on-edit))
:onBlur (fn [e]
(when (om/get-state owner :editing)
(end-edit data edit-key text owner on-edit)))}))
(dom/td nil
(dom/button
#js {:className "btn btn-primary"
:style (display (not editing))
:onClick #(om/set-state! owner :editing true)}
"Edit")))))))
(defn create-class [classes owner]
(let [class-id-el (om/get-node owner "class-id")
class-id (.-value class-id-el)
class-name-el (om/get-node owner "class-name")
class-name (.-value class-name-el)
new-class {:class/id class-id :class/title class-name}]
(om/transact! classes [] #(conj % new-class) [:create new-class])
(set! (.-value class-id-el) "")
(set! (.-value class-name-el) "")))
(defn classes-view [classes owner]
(reify
om/IRender
(render [_]
(dom/div #js {:id "classes"}
(dom/div #js {:className "panel panel-default"}
(dom/div #js {:className "panel-heading"}
(dom/h3 #js {:className "panel-title"} "Classes"))
(dom/div #js {:className "table-responsive"}
(dom/table #js {:className "table table-hover"}
(dom/thead nil
(dom/tr #js {:className "active"}
(dom/th nil "Title")
(dom/th nil)))
(apply dom/tbody nil
(map
(fn [class]
(let [id (:class/id class)]
(om/build editable class
{:opts {:edit-key :class/title}})))
classes))))
(dom/div #js {:className "panel-footer"}
(dom/label nil "ID:")
(dom/span nil "\u00A0")
(dom/input #js {:ref "class-id"})
(dom/span nil "\u00A0")
(dom/label nil "Name:")
(dom/span nil "\u00A0")
(dom/input #js {:ref "class-name"})
(dom/span nil "\u00A0")
(dom/button
#js {:className "btn btn-primary"
:onClick (fn [e] (create-class classes owner))}
"Add")))))))
(defn app-view [app owner]
(reify
om/IWillUpdate
(will-update [_ next-props next-state]
(when (:err-msg next-state)
(js/setTimeout #(om/set-state! owner :err-msg nil) 5000)))
om/IRenderState
(render-state [_ {:keys [err-msg]}]
(dom/div nil
(om/build om-sync (:classes app)
{:opts {:view classes-view
:filter (comp #{:create :update :delete} tx-tag)
:id-key :class/id
:on-success (fn [res tx-data] (println res))
:on-error
(fn [err tx-data]
(reset! app-state (:old-state tx-data))
(om/set-state! owner :err-msg
"Ooops! Sorry something went wrong try again later."))}})
(when err-msg
(dom/div nil err-msg))))))
(let [tx-chan (chan)
tx-pub-chan (async/pub tx-chan (fn [_] :txs))]
(edn-xhr
{:method :get
:url "/init"
:on-complete
(fn [res]
(reset! app-state res)
(om/root app-view app-state
{:target (gdom/getElement "classes")
:shared {:tx-chan tx-pub-chan}
:tx-listen
(fn [tx-data root-cursor]
(put! tx-chan [tx-data root-cursor]))}))}))
(om/root classes-view app-state
{:target (gdom/getElement "classes")})
core.clj
--------
(ns om-async.core
(:require [ring.util.response :refer [file-response]]
[ring.adapter.jetty :refer [run-jetty]]
[ring.middleware.edn :refer [wrap-edn-params]]
[compojure.core :refer [defroutes GET PUT POST]]
[compojure.route :as route]
[compojure.handler :as handler]
[datomic.api :as d]))
(def uri "datomic:free://localhost:4334/om_async")
(def conn (d/connect uri))
(defn index []
(file-response "public/html/index.html" {:root "resources"}))
(defn generate-response [data & [status]]
{:status (or status 200)
:headers {"Content-Type" "application/edn"}
:body (pr-str data)})
(defn get-classes [db]
(->> (d/q '[:find ?class
:where
[?class :class/id]]
db)
(map #(d/touch (d/entity db (first %))))
vec))
(defn init []
(generate-response
{:classes {:url "/classes" :coll (get-classes (d/db conn))}}))
(defn create-class [params]
(let [id (:class/id params)
db (d/db conn)
title (:class/title params)]
(d/transact conn [{:db/id (d/tempid :db.part/user)
:class/title title
:class/id id}])
(generate-response {:status :ok})))
(defn update-class [params]
(let [id (:class/id params)
db (d/db conn)
title (:class/title params)
eid (ffirst
(d/q '[:find ?class
:in $ ?id
:where
[?class :class/id ?id]]
db id))]
(d/transact conn [[:db/add eid :class/title title]])
(generate-response {:status :ok})))
(defn classes []
(generate-response (get-classes (d/db conn))))
(defroutes routes
(GET "/" [] (index))
(GET "/init" [] (init))
(GET "/classes" [] (classes))
(POST "/classes" {params :edn-params} (create-class params))
(PUT "/classes" {params :edn-params} (update-class params))
(route/files "/" {:root "resources/public"}))
(def app
(-> routes
wrap-edn-params))
(defonce server
(run-jetty #'app {:port 8080 :join? false}))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment