Skip to content

Instantly share code, notes, and snippets.

@mattyulrich
Last active May 13, 2016 21:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattyulrich/8524ce8f20606e644770205021875639 to your computer and use it in GitHub Desktop.
Save mattyulrich/8524ce8f20606e644770205021875639 to your computer and use it in GitHub Desktop.
(ns cljs-problem.app
(:require
[goog.dom :as gdom]
[om.next :as om :refer-macros [defui]]
[om.dom :as dom]
[ajax.core :refer [POST]]))
(enable-console-print!)
;(def target-app-state {:current-user {:user-id 123 :user-name "John"}
; :selected-class {:class-id 1 :class-name "Learning Om"
; :teacher {:user-id 456 :user-name "Fred"}
; :students [{:user-id 789 :user-name "Jill"}
; {:user-id 963 :user-name "Mary"}]}
; :classes
; [{:class-id 1 :class-name "Learning Om"
; :teacher {:user-id 456 :user-name "Fred"}
; :students [{:user-id 789 :user-name "Jill"}
; {:user-id 963 :user-name "Mary"}]}
; {:class-id 2 :class-name "Not Being Confused"
; :teacher {:user-id 246 :user-name "Jane"}
; :students [{:user-id 867 :user-name "Carl"}
; {:user-id 309 :user-name "Gretchen"}]}]
; })
(def initial-app-state {})
(defonce initial-state (atom initial-app-state))
(defmulti read om/dispatch)
(defmulti mutate om/dispatch)
(def parser (om/parser {:read read :mutate mutate}))
(def reconciler (om/reconciler {:state initial-state
:parser parser}))
(defn ajax-result-for-user-classes
[]
(let [user-classes [{:class-id 1 :class-name "Learning Om"
:teacher {:user-id 456 :user-name "Fred"}
:students [{:user-id 789 :user-name "Jill"}
{:user-id 963 :user-name "Mary"}]}
{:class-id 2 :class-name "Not Being Confused"
:teacher {:user-id 246 :user-name "Jane"}
:students [{:user-id 867 :user-name "Carl"}
{:user-id 309 :user-name "Gretchen"}]}]]
(om/transact! reconciler `[(classes/set-classes {:classes-for-user ~user-classes}) :classes])))
(defn ajax-result-for-user
[e]
(let [current-user {:user-id 123 :user-name "John"}]
(om/transact! reconciler `[(users/set-current-user {:new-current-user ~current-user}) :current-user])
(ajax-result-for-user-classes)))
(defui Login-Button
Object
(render [this]
(dom/button #js {:className "btn btn-primary" :onClick ajax-result-for-user} "Login")))
(def login (om/factory Login-Button))
(defui Hello-Current-User
Object
(render [this]
(let [{:keys [user-name]} (om/props this)]
(dom/span #js {:className "lead"} (str "Hello " user-name "!")))))
(def hello (om/factory Hello-Current-User))
(defui Header
Object
(render [this]
(let [current-user (om/props this)]
(dom/header #js {:className "navbar navbar-default navbar-fixed-top"}
(dom/div #js {:className "container-fluid"}
(dom/div #js {:className "nav navbar-header navbar-left"}
(dom/h2 nil "CLJS Test"))
(dom/div #js {:className "nav navbar-header navbar-right"}
(dom/div nil
(if (= :not-found current-user)
(login)
(hello current-user)))))))))
(def header-component (om/factory Header))
(defui User
static om/Ident
(ident [this {:keys [user-id]}]
[:user/by-id user-id])
static om/IQuery
(query [this]
[:user-id :user-name])
Object
(render [this]
(let [{:keys [user-name]} (om/props this)]
(dom/span nil user-name))))
(def user-component (om/factory User {:keyfn :user-id}))
(defui Class
static om/Ident
(ident [this {:keys [class-id]}]
[:class/by-id class-id])
static om/IQuery
(query [this]
[:class-id :class-name
{:teacher (om/get-query User)}
{:students (om/get-query User)}])
Object
(render [this]
(let [{:keys [class-id class-name teacher students]} (om/props this)]
(dom/div nil
(dom/div nil
(dom/div nil (dom/span nil "Class Id:")
(dom/span nil class-id))
(dom/div nil (dom/span nil "Class Name:")
(dom/span nil class-name))
(dom/div nil (dom/span nil "Teacher: ")
(user-component teacher))
(dom/div nil (dom/span nil "Students: ")
(dom/ol nil
(map-indexed (fn [idx student]
(dom/li #js {:key (str "stid-" idx)} (user-component student)))
students))))))))
(def class-component (om/factory Class {:keyfn :class-id}))
(defui Class-Select-Option
Object
(render [this]
(let [{:keys [class-id class-name]} (om/props this)]
(dom/option #js {:value class-id} class-name))))
(def class-option (om/factory Class-Select-Option {:keyfn :class-id}))
(defn select-class
[component event]
(let [val (.-value (.-target event))]
(if (= val "unsel")
(om/transact! component `[(classes/unchoose-class) :selected-class])
(let [class-id (js/parseInt val 10)]
(om/transact! component `[(classes/choose-class {:new-selected-class ~class-id}) :selected-class])))))
(defui Class-Select
Object
(render [this]
(let [classes (om/props this)
{:keys [select-option-fn]} (om/get-computed this)]
(dom/div nil
(if (= :not-found classes)
(dom/div nil "No Classes")
(dom/div nil
(dom/select #js {:onChange select-option-fn}
(dom/option #js {:value "unsel"} "Please select a class: ")
(map #(class-option %) classes))))))))
(def class-select-component (om/factory Class-Select))
(defui App
static om/IQuery
(query [this]
[{:current-user (om/get-query User)}
{:classes (om/get-query Class)}
{:selected-class (om/get-query Class)}
])
Object
(render [this]
(let [{:keys [current-user classes selected-class]} (om/props this)]
(dom/div nil
(header-component current-user)
(dom/div #js {:className "offset-top"}
(if (= classes :not-found)
(dom/div nil nil)
(class-select-component (om/computed classes {:select-option-fn (partial select-class this)})))
(if (= selected-class :not-found)
(dom/div nil nil)
(class-component selected-class)))))))
(defmethod read :current-user
[{:keys [state query]} key _]
(let [st @state]
(if (nil? (get st key))
{:value :not-found}
(let [val (om/db->tree query (get st key) st)]
{:value val}))))
(defmethod read :classes
[{:keys [state query]} key _]
(let [st @state]
(if (nil? (get st key))
{:value :not-found}
(let [val (om/db->tree query (get st key) st)]
{:value val}))))
(defmethod read :selected-class
[{:keys [state query]} key _]
(let [st @state]
(if (nil? (get st key))
{:value :not-found}
(let [val (om/db->tree query (get st key) st)]
{:value val}))))
(defmethod mutate 'users/set-current-user
[{:keys [state]} _ {:keys [new-current-user]}]
(let [db-vals (om/tree->db App {:current-user new-current-user} true)]
{:value :current-user
:action #(swap! state merge db-vals)}))
(defmethod mutate 'classes/set-classes
[{:keys [state]} _ {:keys [classes-for-user]}]
(let [db-vals (om/tree->db App {:classes classes-for-user} true)]
{:value :classes
:action #(swap! state merge db-vals)}))
(defmethod mutate 'classes/unchoose-class
[{:keys [state]} _ _]
{:value :selected-class
:action #(swap! state dissoc :selected-class)})
(defmethod mutate 'classes/choose-class
[{:keys [state]} _ {:keys [new-selected-class]}]
{:value :selected-class
:action #(swap! state assoc :selected-class [:class/by-id new-selected-class])})
(om/add-root! reconciler App (gdom/getElement "app"))
@anmonteiro
Copy link

(def target-app-state
  {:current-user {:user-id 123 :user-name "John"}
   :selected-class {:class-id 1 :class-name "Learning Om"
                    :teacher {:user-id 456 :user-name "Fred"}
                    :students [{:user-id 789 :user-name "Jill"}
                               {:user-id 963 :user-name "Mary"}]}
   :classes [{:class-id 1 :class-name "Learning Om"
              :teacher {:user-id 456 :user-name "Fred"}
              :students [{:user-id 789 :user-name "Jill"}
                         {:user-id 963 :user-name "Mary"}]}
             {:class-id 2 :class-name "Not Being Confused"
              :teacher {:user-id 246 :user-name "Jane"}
              :students [{:user-id 867 :user-name "Carl"}
                         {:user-id 309 :user-name "Gretchen"}]}]})

(defmulti read om/dispatch)

(defmethod read :current-user
  [{:keys [state query]} key _]
  (let [st @state]
    (if-let [val (om/db->tree query (get st key) st)]
      {:value val}
      {:value :not-found})))

(defmethod read :classes
  [{:keys [state query]} key _]
  (let [st @state]
    (if-let [val (om/db->tree query (get st key) st)]
      {:value val}
      {:value :not-found})))

(defmethod read :selected-class
  [{:keys [state query]} key _]
  (let [st @state]
    (if-let [val (om/db->tree query (get st key) st)]
      {:value val}
      {:value :not-found})))

(defmulti mutate om/dispatch)

;; anmonteiro: changed this method
(defmethod mutate 'classes/choose-class
  [{:keys [state]} _ {:keys [new-selected-class]}]
  (println "New Selected: " new-selected-class)
  (let [selected-class [:class/by-id (js/parseInt new-selected-class 10)]]
    {:value {:keys [:selected-class]}
     :action #(swap! state assoc :selected-class selected-class)}))

(def parser (om/parser {:read read :mutate mutate}))

(def reconciler (om/reconciler {:state target-app-state
                                :parser parser}))

(defui Header
  static om/Ident
  (ident [this {:keys [user-id]}]
    [:user/by-id user-id])
  static om/IQuery
  (query [this]
    [:user-id :user-name])
  Object
  (render [this]
    (let [{:keys [user-name]} (om/props this)]
      (dom/header #js {:className "navbar navbar-default navbar-fixed-top"}
                 (dom/div #js {:className "container-fluid"}
                          (dom/div #js {:className "nav navbar-header navbar-left"}
                                   (dom/h2 nil "CLJS Test"))
                          (dom/div #js {:className "nav navbar-header navbar-right"}
                                   (dom/div nil
                                            (dom/span #js {:className "lead"} (str "Hello " user-name "!")))))))))

(def header-component (om/factory Header))

(defui User
  static om/Ident
  (ident [this {:keys [user-id]}]
    [:user/by-id user-id])
  static om/IQuery
  (query [this]
    [:user-id :user-name])
  Object
  (render [this]
    (let [{:keys [user-name]} (om/props this)]
      (dom/span nil user-name))))

(def user-component (om/factory User {:keyfn :user-id}))

(defui Class
  static om/Ident
  (ident [this {:keys [class-id]}]
    [:class/by-id class-id])
  static om/IQuery
  (query [this]
    [:class-id :class-name
     {:teacher (om/get-query User)}
     {:students (om/get-query User)}])
  Object
  (render [this]
    (let [{:keys [class-id class-name teacher students]} (om/props this)]
      (dom/div nil
               (dom/div nil
                        (dom/div nil (dom/span nil "Class Id:")
                                 (dom/span nil class-id))
                        (dom/div nil (dom/span nil "Class Name:")
                                 (dom/span nil class-name))
                        (dom/div nil (dom/span nil "Teacher: ")
                                 (user-component teacher))
                        (dom/div nil (dom/span nil "Students: ")
                                 (dom/ol nil
                                         (map-indexed (fn [idx student]
                                                        (dom/li #js {:key (str "stid-" idx)} (user-component student)))
                                                      students))))))))

(def class-component (om/factory Class {:keyfn :class-id}))

(defui Class-Select-Option
  ;; anmonteiro: removed ident & iquery
  Object
  (render [this]
    (let [{:keys [class-id class-name]} (om/props this)]
      (dom/option #js {:value class-id} class-name))))

(def class-option (om/factory Class-Select-Option {:keyfn :class-id}))

;; anmonteiro: changed this method
(defn select-class
  [component event]
  ;; This is the trouble bit
  (let [idx (.. event -target -options -selectedIndex)
        value (aget (.. event -target -options) idx "value")]
    (om/transact! component `[(classes/choose-class {:new-selected-class ~value}) :selected-class])))

(defui Class-Select
  ;; anmonteiro: removed ident & iquery
  Object
  (render [this]
    (let [classes (om/props this)
          {:keys [select-on-change]} (om/get-computed this)]
      (println "Classes: " classes)
      (dom/div nil
        (if (= :not-found classes)
          (dom/div nil "No Classes")
          (dom/div nil
            (dom/select #js {:onChange select-on-change}
              (map #(class-option %) classes))))))))

(def class-select-component (om/factory Class-Select))

(defui App
  static om/IQuery
  (query [this]
    [{:current-user (om/get-query Header)}
     {:classes (om/get-query Class)}
     {:selected-class (om/get-query Class)}])
  Object
  (render [this]
    (let [{:keys [current-user selected-class classes]} (om/props this)]
      (dom/div nil
        (header-component current-user)
        (dom/div #js {:className "offset-top"}
          ;; anmonteiro: changed this part too
          (class-select-component (om/computed classes {:select-on-change (partial select-class this)}))
          (class-component selected-class))))))

(om/add-root! reconciler App (gdom/getElement "app"))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment