Created
June 13, 2016 07:41
-
-
Save IwanKaramazow/115de05f35277754104f7eb47c06590c to your computer and use it in GitHub Desktop.
Working solution.cljs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns listdetail.core | |
(:require [goog.dom :as gdom] | |
[om.next :as om :refer-macros [defui]] | |
[om.dom :as dom] | |
[clojure.set :as set])) | |
(enable-console-print!) | |
(def app-state {}) | |
(defmulti read om/dispatch) | |
(defmethod read :projects | |
[{:keys [state ast query query-root]} k _] | |
(let [st @state] | |
(if-let [x (get-in st [:active-props k])] | |
{:value (om/db->tree query x st)} | |
{:remote (assoc ast :query-root true)}))) | |
(defmethod read :project/detail | |
[{:keys [ast state query target]} key {:keys [id]}] | |
(let [st @state | |
id (if id id (:project/selected st))] | |
(when id | |
(let [val (om/db->tree query [:project/by-id id] st) | |
available (keys val) | |
diffed-query (-> | |
(into [] (set/difference (set query) (set available))) | |
(conj :id))] | |
(if (= available query) | |
{:value val} | |
{:value val | |
:remote (-> ast | |
(assoc :query diffed-query) | |
(assoc :query-root true)) }))))) | |
(defmulti mutate om/dispatch) | |
(defmethod mutate 'project/select | |
[{:keys [state]} key {:keys [id]}] | |
{:action #(swap! state assoc :project/selected id)}) | |
(defmethod read :active-props | |
[{:keys [parser query ast target] :as env} key _] | |
(let [remote (parser env query target)] | |
(if (and target (not-empty remote)) | |
{:remote (update-in ast [:query] (fn [query] remote))} | |
{:value (parser env query)}))) | |
;; components | |
(defui ProjectListItem | |
static om/Ident | |
(ident [this {:keys [id]}] | |
[:project/by-id id]) | |
static om/IQuery | |
(query [_] | |
[:id :title]) | |
Object | |
(render [this] | |
(let [{:keys [id title]} (om/props this) | |
{:keys [select] } (om/get-computed this)] | |
(dom/li #js {:onClick (fn [e] | |
(select id))} title)))) | |
(def projectListItem (om/factory ProjectListItem)) | |
(defn project-list [projects select] | |
(dom/div nil | |
(dom/h3 nil "Project list:") | |
(dom/ul nil | |
(map #(projectListItem (om/computed % {:select select})) projects)))) | |
(defui ProjectDetail | |
static om/Ident | |
(ident [this {:keys [id]}] | |
[:project/by-id id]) | |
static om/IQuery | |
(query [_] | |
[:id :title :description]) | |
Object | |
(render [this] | |
(let [{:keys [id title description]} (om/props this)] | |
(dom/div nil | |
(dom/h3 nil "Project Detail") | |
(if (nil? id) | |
(dom/div nil "No project selected.") | |
(dom/div nil | |
(dom/div nil "Selected project: " id) | |
(dom/div nil "Title: " title) | |
(dom/div nil "Description: " description))))))) | |
(def project-detail (om/factory ProjectDetail)) | |
(defn select-project [component] | |
(fn [id] | |
(om/transact! component `[(project/select {:id ~id})]) | |
(om/update-query! component | |
(fn [{:keys [query params] :as q}] | |
(update q :params assoc :id id)) ))) | |
(defui Wrapper | |
static om/IQueryParams | |
(params [this] | |
{:id nil}) | |
static om/IQuery | |
(query [_] | |
`[{:projects ~(om/get-query ProjectListItem)} | |
({:project/detail ~(om/get-query ProjectDetail)} {:id ~'?id})]) | |
Object | |
(render [this] | |
(let [{:keys [projects project/detail]} (om/props this)] | |
(dom/div nil | |
(project-list projects (select-project this)) | |
(project-detail detail))))) | |
(def wrapper (om/factory Wrapper)) | |
(defui Root | |
static om/IQueryParams | |
(params | |
[this] | |
{:active-query []}) | |
static om/IQuery | |
(query | |
[this] | |
'[{:active-props ?active-query}]) | |
Object | |
(componentWillMount | |
[this] | |
(om/set-query! this | |
{:params {:active-query (om/get-query Wrapper)}})) | |
(render | |
[this] | |
(let [{:keys [active-props] :as props} (om/props this)] | |
(dom/div {} (wrapper active-props))))) | |
;; fake backend | |
(def fake-database (atom {:projects [[:project/by-id 1] [:project/by-id 2]], :project/by-id {1 {:id 1, :title "Learn Haskell" :description "How do I get started with Haskell?"}, 2 {:id 2, :title "Learn Scheme" :description "Lisp rocks!"}}})) | |
(defmulti backend-read om/dispatch) | |
(defmethod backend-read :projects | |
[{:keys [database query]} key {:keys [id]}] | |
(let [db @database] | |
{:value (om/db->tree query (:projects db) db)})) | |
(defmethod backend-read :project/detail | |
[{:keys [database query]} key {:keys [id]}] | |
(let [db @database] | |
{:value (om/db->tree query [:project/by-id id] db)})) | |
(def backend-parser (om/parser {:read backend-read})) | |
;; fake a send to the fake backend | |
(defn send [{:keys [remote]} merge] | |
(let [{:keys [query rewrite]} (om/process-roots remote) | |
_ (println "incoming query" query)] | |
(merge (rewrite (backend-parser {:database fake-database} query))))) | |
;; do not throw away the app state when merging | |
(defn deep-merge [& xs] | |
"Merges nested maps without overwriting existing keys." | |
(if (every? map? xs) | |
(apply merge-with deep-merge xs) | |
(last xs))) | |
;;reconciler | |
(def reconciler | |
(om/reconciler | |
{:state app-state | |
:parser (om/parser {:read read :mutate mutate}) | |
:send send | |
:merge-tree deep-merge})) | |
(om/add-root! reconciler | |
Root (gdom/getElement "app")) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment