SlideShare a Scribd company logo
Reactive data visualisations with Om
Anna Pawlicka
Data Engineer
@AnnaPawlicka
Saturday, 28 June 14
Technologies
Saturday, 28 June 14
D3 (Data-Driven Documents)
[to visualise data]
• Data bound to DOM
• Interactive - transformations driven by data
• Huge community
• Higher level libraries available
Saturday, 28 June 14
Leaflet.js & Dimple.js
[higher level libraries]
• Open-source Java-Script libraries
• Interactive
• Simple API
• Access to underlying D3 functions
Saturday, 28 June 14
Facebook’s React
[interface components]
• Solves complex UI rendering
• Declarative framework
• No to “two-way data binding”
• Re-renders the entire UI
Saturday, 28 June 14
U can’t touch this
[a.k.a. Virtual DOM]
• Developer describes the document tree
• React :
• Maintains virtual DOM
• Diffs between previous and next renders of a UI
• Less code
• Shorter time to update
Saturday, 28 June 14
Om Nom Nom Nom
[because we prefer Clojure]
• Entire state of the UI in a single piece of data
• Immutable data structures = Reference equality check
• No need to worry about optimisation
• Snapshottable
• Free undo
Saturday, 28 June 14
Component life cycle protocols
IWillMount
IRenderState
IShouldUpdateIInitState
IRender
Saturday, 28 June 14
Liberator & core.async
[component interaction]
• Provide API to access external components (e.g. database):
(defresource hello-world
:available-media-types ["text/plain"]
:allowed-methods [:get]
:handle-ok (fn [_] "Hello, world.”))
• Send/receive messages between components using core.async channels:
(let [ch (chan)]
(go (while true
(let [v (<! ch)]
(prn "Vader: " v))))
(go (>! ch "No, I am your father")
(<! (timeout 5000))
(>! ch "Search your feelings; you know it to be true!")))
Saturday, 28 June 14
Pretty charts
Saturday, 28 June 14
device_id | type | timestamp | value
------------------------------------------+------------------------+---------------------------------
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:00:00+0000 | 8
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:05:00+0000 | 46
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:10:00+0000 | 23
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:15:00+0000 | 20
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:20:00+0000 | 67
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:25:00+0000 | 70
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:30:00+0000 | 10
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:35:00+0000 | 42
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:40:00+0000 | 95
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:45:00+0000 | 16
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:50:00+0000 | 79
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:55:00+0000 | 33
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:00:00+0000 | 45
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:05:00+0000 | 85
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:10:00+0000 | 32
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:15:00+0000 | 7
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:20:00+0000 | 92
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:25:00+0000 | 15
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:30:00+0000 | 9
8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:35:00+0000 | 73
Saturday, 28 June 14
Chart & API
Saturday, 28 June 14
(defresource measurements-resource [id type ctx]
:allowed-methods #{:get}
:available-media-types ["application/edn"]
:handle-ok (partial retrieve-measurements id type))
(defresource devices-resource [_]
:allowed-methods #{:get}
:known-content-type? #{"application/edn"}
:available-media-types #{"application/edn"}
:handle-ok retrieve-devices)
(defroutes app-routes
(ANY "/devices/" [] devices-resource)
(ANY "/device/:id/type/:type/measurements/" [id type] (measurements-resource
id type))
(route/not-found "Not Found"))
(def app
(handler/site app-routes))
Saturday, 28 June 14
(def app-model
(atom {:devices {:all []}
:chart {:data []}}))
(om/root measurements-chart app-model
{:target (.getElementById js/document "app")
:shared {:url "http://localhost:3000/"}})
Saturday, 28 June 14
(defn measurements-chart [cursor owner]
(reify
om/IInitState
(init-state [_]
{:chans {:event-chan (chan (sliding-buffer 1))}})
om/IRenderState
(render-state [_ {:keys [chans]}]
(dom/div nil
(om/build device-form (:devices cursor)
{:init-state chans})
(om/build chart/chart-figure
(:chart cursor)
{:init-state chans
:opts {:event-fn get-measurements
:chart {:div {:id "chart"
:width "100%" :height 600}
:bounds {:x "5%" :y "15%"
:width "80%" :height "50%"}
:x-axis "timestamp"
:y-axis "value"
:plot js/dimple.plot.line}}})))))
Initialise core.async
channel
Saturday, 28 June 14
(defn measurements-chart [cursor owner]
(reify
om/IInitState
(init-state [_]
{:chans {:event-chan (chan (sliding-buffer 1))}})
om/IRenderState
(render-state [_ {:keys [chans]}]
(dom/div nil
(om/build device-form (:devices cursor)
{:init-state chans})
(om/build chart/chart-figure
(:chart cursor)
{:init-state chans
:opts {:event-fn get-measurements
:chart {:div {:id "chart"
:width "100%" :height 600}
:bounds {:x "5%" :y "15%"
:width "80%" :height "50%"}
:x-axis "timestamp"
:y-axis "value"
:plot js/dimple.plot.line}}})))))
This is how you construct
components
Triggered on arrival of a
new message
Saturday, 28 June 14
(defn device-form
[cursor owner]
(reify
om/IWillMount
(will-mount [_]
(let [host (:url (om/get-shared owner))
url (str host "devices/")]
(GET url {:handler #(om/update! cursor [:all] %)})))
om/IRenderState
(render-state [_ {:keys [event-chan]}]
(let [devices (:all cursor)]
(dom/div nil
(dom/table nil
(dom/thead nil (dom/tr nil
(dom/th nil "Select")
(dom/th nil "ID")
(dom/th nil "Type")
(dom/th nil "Description")
(dom/th nil "Unit")))
(apply dom/tbody nil
(om/build-all (form-row event-chan)
devices))))))))
Sequence of components
Saturday, 28 June 14
(defn form-row [event-chan]
(fn [the-item owner]
(om/component
(let [{:keys [id type description unit]} the-item]
(dom/tr nil
(dom/td nil
(dom/input #js {:type "radio"
:name "type"
:value name
:onChange
(fn [e]
(put! event-chan
{:id id
:type type}))}))
(dom/td nil id)
(dom/td nil type)
(dom/td nil description)
(dom/td nil unit))))))
Send message down the
queue
Saturday, 28 June 14
(defn chart-figure [cursor owner {:keys [chart] :as opts}]
(reify
om/IWillMount
(will-mount [_]
(let [event-chan (om/get-state owner [:event-chan])
event-fn (:event-fn opts)]
(go (while true
(let [v (<! event-chan)]
(event-fn cursor owner v))))))
om/IRender
(render [_]
(let [{:keys [id width height]} (:div chart)]
(dom/div #js {:id id :width width :height height})))
om/IDidUpdate
(did-update [_ _ _]
(let [n (.getElementById js/document "chart")]
(while (.hasChildNodes n)
(.removeChild n (.-lastChild n))))
(when (:data cursor)
(draw-chart cursor chart)))))
Reads the message from
the queue
Saturday, 28 June 14
(defn get-measurements [cursor owner message]
(let [host (:url (om/get-shared owner))
{:keys [id type]} message
url (str host "device/" id "/type/" type "/
measurements/")]
(GET url {:handler #(om/update! cursor [:data] %)})))
Saturday, 28 June 14
(defn draw-chart [cursor {:keys [div bounds x-axis y-axis plot]}]
(let [{:keys [id width height]} div
Chart (.-chart js/dimple)
svg (.newSvg js/dimple (str "#" id) width height)
data (get-in cursor [:data])
dimple-chart (.setBounds (Chart. svg) (:x bounds) (:y bounds)
(:width bounds) (:height bounds))
x (.addCategoryAxis dimple-chart "x" x-axis)
y (.addMeasureAxis dimple-chart "y" y-axis)
s (.addSeries dimple-chart nil plot (clj->js [x y]))]
(aset s "data" (clj->js data))
(.addLegend dimple-chart "5%" "10%" "20%" "10%" "right")
(.draw dimple-chart)))
Saturday, 28 June 14
Last.fm chart
Saturday, 28 June 14
(def app-model
(atom {:username-box {:username ""}
:chart {:data []}}))
(om/root lastfm-chart app-model
{:target (.getElementById js/document "app")
:shared {:api-root
"http://ws.audioscrobbler.com/2.0/"}})
Saturday, 28 June 14
(defn lastfm-chart [cursor owner]
(reify
om/IInitState
(init-state [_]
{:chans {:event-chan (chan (sliding-buffer 1))}})
om/IRenderState
(render-state [_ {:keys [chans]}]
(dom/div nil
(dom/div #js {:className "container"}
(dom/h3 nil "Last.fm chart")
(om/build forms/input-box
(:username-box cursor)
{:init-state chans})
(dom/div #js {:className "well" :style #js {:width "100%" :height 600}}
(om/build chart/chart-figure
(:chart cursor)
{:init-state chans
:opts {:event-fn get-all-artists
:chart {:div {:id "chart"
:width "100%" :height 600}
:bounds {:x "5%" :y "15%"
:width "80%" :height "50%"}
:x-axis "name"
:y-axis "playcount"
:plot js/dimple.plot.bar}}})))))))
Username input and chart
components
Saturday, 28 June 14
(defn get-all-artists [cursor owner username]
(let [api-root (:api-root (om/get-shared owner))
url (str api-root
"?method=user.gettopartists&user="
username "&api_key="
api-key "&format=json")]
(GET url {:handler #(om/update! cursor [:data]
(get-in % ["topartists" "artist"]))})))
Saturday, 28 June 14
(defn send-value [owner event-chan]
(let [value (om/get-state owner :value)]
(put! event-chan value)))
(defn input-box [cursor owner]
(reify
om/IRenderState
(render-state [_ {:keys [event-chan]}]
(dom/div #js {:className "form-inline" :role "form"}
(dom/div #js {:className "form-group"}
(dom/input
#js {:type "text"
:className "form-control"
:style #js {:width "100%"}
:onChange (fn [e]
(om/set-state! owner :value
(.-value (.-target e))))
:onKeyPress (fn [e]
(when (= (.-keyCode e) 13)
(send-value owner event-chan)))}))
(dom/button #js {:type "button" :className "btn btn-primary"
:onClick (fn [e]
(send-value owner event-chan)} "Go")))))
Saturday, 28 June 14
Interactive maps
Saturday, 28 June 14
Leaflet map & geocoding
Saturday, 28 June 14
(def app-model
(atom
{:map {:leaflet-map nil
:map {:lat 50.06297958283694 :lng 19.94705200195313}}
:panel {:coordinates nil}}))
(om/root geocoded-map app-model {:target (. js/document (getElementById "app"))})
Saturday, 28 June 14
(defn geocoded-map
[cursor owner]
(reify
om/IInitState
(init-state [_]
{:chans {:event-chan (chan (sliding-buffer 1))
:pin-chan (chan (sliding-buffer 1))}})
om/IRenderState
(render-state [_ {:keys [chans]}]
(dom/div nil
(om/build map-component (:map cursor) {:init-state chans})
(om/build panel-component (:panel cursor) {:init-state chans})))))
Saturday, 28 June 14
(defn map-component [cursor owner]
(reify
om/IWillMount
(will-mount [_]
(let [event-chan (om/get-state owner [:event-chan])]
(go (while true
(let [v (<! event-chan)]
(pan-to-postcode cursor owner v))))))
om/IRender
(render [this]
(dom/div #js {:id "map"}))
om/IDidMount
(did-mount [this]
(let [node (om/get-node owner)
{:keys [leaflet-map] :as map} (create-map (:map cursor) node)
loc {:lng (get-in cursor [:map :lng])
:lat (get-in cursor [:map :lat])}]
(.on leaflet-map "click" (fn [e]
(let [latlng (.-latlng e)]
(drop-pin owner leaflet-map latlng))))
(.panTo leaflet-map (clj->js loc))
(om/update! cursor :leaflet-map leaflet-map)))))
Creates map and stores it in
app state
Saturday, 28 June 14
(defn pan-to-postcode [cursor owner postcode]
(let [postcode (.toUpperCase (string/replace postcode #"[s]+" ""))
url (str geocoding-api-root postcode)]
(GET url {:handler
(fn [body]
(let [map (:leaflet-map @cursor)
{:keys [lat lng]} (location-from-response body)]
(.panTo map (clj->js {:lat (js/parseFloat lat)
:lng (js/parseFloat lng)}))))})))
(defn drop-pin [owner map latlng]
(let [marker (-> (.addTo (.marker js/L (clj->js latlng)) map))
pin-chan (om/get-state owner [:pin-chan])]
(put! pin-chan {:action :put :coordinates latlng})
(.on marker "click" (fn [e] (.removeLayer map marker)
(put! pin-chan {:action :remove})))))
Saturday, 28 June 14
(defn panel-component [cursor owner]
(reify
om/IWillMount
(will-mount [_]
(let [pin-chan (om/get-state owner [:pin-chan])]
(go (while true
(let [{:keys [action coordinates]} (<! pin-chan)]
(if (= action :put)
(om/update! cursor [:coordinates] coordinates)
(om/update! cursor [:coordinates] nil)))))))
om/IRender
(render [_]
(let [event-chan (om/get-state owner [:event-chan])]
(dom/div #js {:id "panel"}
(dom/h3 nil "Postcode lookup")
(om/build forms/input-box cursor
{:init-state {:event-chan event-chan}})
(om/build coordinates-component (:coordinates cursor)))))))
Saturday, 28 June 14
(defn coordinates-component [cursor owner]
(om/component
(dom/section nil
(dom/h3 nil "Coordinates")
(dom/p nil "(Click anywhere on a map)")
(when cursor
(dom/div nil
(dom/label nil (str "Lat: " (.-lat cursor)))
(dom/label nil (str "Lng: " (.-lng cursor))))))))
Saturday, 28 June 14
Summary
• You can leverage all of JavaScript and ClojureScript functionality
and combine them with Om
• Fast rendering and interactivity
• Immutability = efficiency
• Sane application structure
• Reusability
Saturday, 28 June 14
Thank you!
Saturday, 28 June 14

More Related Content

What's hot

Reactive Collections
Reactive CollectionsReactive Collections
Reactive Collections
Aleksandar Prokopec
 
JQuery Flot
JQuery FlotJQuery Flot
JQuery Flot
Arshavski Alexander
 
A Computational View of MapReduce
A Computational View of MapReduceA Computational View of MapReduce
A Computational View of MapReduce
Zilong Tan
 
Functional streams with Kafka - A comparison between Akka-streams and FS2
Functional streams with Kafka - A comparison between Akka-streams and FS2Functional streams with Kafka - A comparison between Akka-streams and FS2
Functional streams with Kafka - A comparison between Akka-streams and FS2
Luis Miguel Reis
 
Clojutre Real Life (2012 ClojuTRE Retro Edition)
Clojutre Real Life (2012 ClojuTRE Retro Edition)Clojutre Real Life (2012 ClojuTRE Retro Edition)
Clojutre Real Life (2012 ClojuTRE Retro Edition)
Metosin Oy
 
Programming the cloud with Skywriting
Programming the cloud with SkywritingProgramming the cloud with Skywriting
Programming the cloud with Skywriting
Derek Murray
 
rx.js make async programming simpler
rx.js make async programming simplerrx.js make async programming simpler
rx.js make async programming simpler
Alexander Mostovenko
 
Nu program language on Shibuya.lisp#5 LT
Nu program language on  Shibuya.lisp#5 LTNu program language on  Shibuya.lisp#5 LT
Nu program language on Shibuya.lisp#5 LT
Yuumi Yoshida
 
Yavorsky
YavorskyYavorsky
Functional Programming from OO perspective (Sayeret Lambda lecture)
Functional Programming from OO perspective (Sayeret Lambda lecture)Functional Programming from OO perspective (Sayeret Lambda lecture)
Functional Programming from OO perspective (Sayeret Lambda lecture)
Ittay Dror
 
Micro and moblile: Java on the Raspberry Pi
Micro and moblile: Java on the Raspberry PiMicro and moblile: Java on the Raspberry Pi
Micro and moblile: Java on the Raspberry Pi
Pance Cavkovski
 
Add Some Fun to Your Functional Programming With RXJS
Add Some Fun to Your Functional Programming With RXJSAdd Some Fun to Your Functional Programming With RXJS
Add Some Fun to Your Functional Programming With RXJS
Ryan Anklam
 
Data Love Conference - Window Functions for Database Analytics
Data Love Conference - Window Functions for Database AnalyticsData Love Conference - Window Functions for Database Analytics
Data Love Conference - Window Functions for Database Analytics
Dave Stokes
 
How to build a html5 websites.v1
How to build a html5 websites.v1How to build a html5 websites.v1
How to build a html5 websites.v1
Bitla Software
 
Bindings: the zen of montage
Bindings: the zen of montageBindings: the zen of montage
Bindings: the zen of montage
Kris Kowal
 
Deep learning study 3
Deep learning study 3Deep learning study 3
Deep learning study 3
San Kim
 
The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31
Mahmoud Samir Fayed
 
mobl
moblmobl
The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196
Mahmoud Samir Fayed
 

What's hot (19)

Reactive Collections
Reactive CollectionsReactive Collections
Reactive Collections
 
JQuery Flot
JQuery FlotJQuery Flot
JQuery Flot
 
A Computational View of MapReduce
A Computational View of MapReduceA Computational View of MapReduce
A Computational View of MapReduce
 
Functional streams with Kafka - A comparison between Akka-streams and FS2
Functional streams with Kafka - A comparison between Akka-streams and FS2Functional streams with Kafka - A comparison between Akka-streams and FS2
Functional streams with Kafka - A comparison between Akka-streams and FS2
 
Clojutre Real Life (2012 ClojuTRE Retro Edition)
Clojutre Real Life (2012 ClojuTRE Retro Edition)Clojutre Real Life (2012 ClojuTRE Retro Edition)
Clojutre Real Life (2012 ClojuTRE Retro Edition)
 
Programming the cloud with Skywriting
Programming the cloud with SkywritingProgramming the cloud with Skywriting
Programming the cloud with Skywriting
 
rx.js make async programming simpler
rx.js make async programming simplerrx.js make async programming simpler
rx.js make async programming simpler
 
Nu program language on Shibuya.lisp#5 LT
Nu program language on  Shibuya.lisp#5 LTNu program language on  Shibuya.lisp#5 LT
Nu program language on Shibuya.lisp#5 LT
 
Yavorsky
YavorskyYavorsky
Yavorsky
 
Functional Programming from OO perspective (Sayeret Lambda lecture)
Functional Programming from OO perspective (Sayeret Lambda lecture)Functional Programming from OO perspective (Sayeret Lambda lecture)
Functional Programming from OO perspective (Sayeret Lambda lecture)
 
Micro and moblile: Java on the Raspberry Pi
Micro and moblile: Java on the Raspberry PiMicro and moblile: Java on the Raspberry Pi
Micro and moblile: Java on the Raspberry Pi
 
Add Some Fun to Your Functional Programming With RXJS
Add Some Fun to Your Functional Programming With RXJSAdd Some Fun to Your Functional Programming With RXJS
Add Some Fun to Your Functional Programming With RXJS
 
Data Love Conference - Window Functions for Database Analytics
Data Love Conference - Window Functions for Database AnalyticsData Love Conference - Window Functions for Database Analytics
Data Love Conference - Window Functions for Database Analytics
 
How to build a html5 websites.v1
How to build a html5 websites.v1How to build a html5 websites.v1
How to build a html5 websites.v1
 
Bindings: the zen of montage
Bindings: the zen of montageBindings: the zen of montage
Bindings: the zen of montage
 
Deep learning study 3
Deep learning study 3Deep learning study 3
Deep learning study 3
 
The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31
 
mobl
moblmobl
mobl
 
The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196
 

Viewers also liked

Optimizing AngularJS Application
Optimizing AngularJS ApplicationOptimizing AngularJS Application
Optimizing AngularJS Application
Md. Ziaul Haq
 
Habits of Effective Designers
Habits of Effective DesignersHabits of Effective Designers
Habits of Effective Designers
DUSPviz
 
Habits of Effective Designers - Handout
Habits of Effective Designers - HandoutHabits of Effective Designers - Handout
Habits of Effective Designers - Handout
DUSPviz
 
Art of-presentations
Art of-presentationsArt of-presentations
Art of-presentations
DUSPviz
 
Mapping with Adobe CC
Mapping with Adobe CCMapping with Adobe CC
Mapping with Adobe CC
DUSPviz
 
Intro to Adobe Illustrator
Intro to Adobe IllustratorIntro to Adobe Illustrator
Intro to Adobe Illustrator
DUSPviz
 
Javascript Best Practices
Javascript Best PracticesJavascript Best Practices
Javascript Best Practices
Christian Heilmann
 

Viewers also liked (7)

Optimizing AngularJS Application
Optimizing AngularJS ApplicationOptimizing AngularJS Application
Optimizing AngularJS Application
 
Habits of Effective Designers
Habits of Effective DesignersHabits of Effective Designers
Habits of Effective Designers
 
Habits of Effective Designers - Handout
Habits of Effective Designers - HandoutHabits of Effective Designers - Handout
Habits of Effective Designers - Handout
 
Art of-presentations
Art of-presentationsArt of-presentations
Art of-presentations
 
Mapping with Adobe CC
Mapping with Adobe CCMapping with Adobe CC
Mapping with Adobe CC
 
Intro to Adobe Illustrator
Intro to Adobe IllustratorIntro to Adobe Illustrator
Intro to Adobe Illustrator
 
Javascript Best Practices
Javascript Best PracticesJavascript Best Practices
Javascript Best Practices
 

Similar to Reactive data visualisations with Om

Om nom nom nom
Om nom nom nomOm nom nom nom
Om nom nom nom
Anna Pawlicka
 
MongoDB for Time Series Data: Analyzing Time Series Data Using the Aggregatio...
MongoDB for Time Series Data: Analyzing Time Series Data Using the Aggregatio...MongoDB for Time Series Data: Analyzing Time Series Data Using the Aggregatio...
MongoDB for Time Series Data: Analyzing Time Series Data Using the Aggregatio...
MongoDB
 
Web components with java by Haijian Wang
Web components with java by Haijian WangWeb components with java by Haijian Wang
Web components with java by Haijian Wang
GWTcon
 
Svcc 2013-d3
Svcc 2013-d3Svcc 2013-d3
Svcc 2013-d3
Oswald Campesato
 
SVCC 2013 D3.js Presentation (10/05/2013)
SVCC 2013 D3.js Presentation (10/05/2013)SVCC 2013 D3.js Presentation (10/05/2013)
SVCC 2013 D3.js Presentation (10/05/2013)
Oswald Campesato
 
Big Data for each one of us
Big Data for each one of usBig Data for each one of us
Big Data for each one of us
OSCON Byrum
 
PyCon SG x Jublia - Building a simple-to-use Database Management tool
PyCon SG x Jublia - Building a simple-to-use Database Management toolPyCon SG x Jublia - Building a simple-to-use Database Management tool
PyCon SG x Jublia - Building a simple-to-use Database Management tool
Crea Very
 
Visualization of Big Data in Web Apps
Visualization of Big Data in Web AppsVisualization of Big Data in Web Apps
Visualization of Big Data in Web Apps
EPAM
 
Reactive programming every day
Reactive programming every dayReactive programming every day
Reactive programming every day
Vadym Khondar
 
Monitoring Your ISP Using InfluxDB Cloud and Raspberry Pi
Monitoring Your ISP Using InfluxDB Cloud and Raspberry PiMonitoring Your ISP Using InfluxDB Cloud and Raspberry Pi
Monitoring Your ISP Using InfluxDB Cloud and Raspberry Pi
InfluxData
 
Compose Async with RxJS
Compose Async with RxJSCompose Async with RxJS
Compose Async with RxJS
Kyung Yeol Kim
 
SVGD3Angular2React
SVGD3Angular2ReactSVGD3Angular2React
SVGD3Angular2React
Oswald Campesato
 
Interactively querying Google Analytics reports from R using ganalytics
Interactively querying Google Analytics reports from R using ganalyticsInteractively querying Google Analytics reports from R using ganalytics
Interactively querying Google Analytics reports from R using ganalytics
Johann de Boer
 
React table tutorial project setup, use table, and usefilter
React table tutorial project setup, use table, and usefilterReact table tutorial project setup, use table, and usefilter
React table tutorial project setup, use table, and usefilter
Katy Slemon
 
ESRI Dev Meetup: Building Distributed JavaScript Map Widgets
ESRI Dev Meetup: Building Distributed JavaScript Map WidgetsESRI Dev Meetup: Building Distributed JavaScript Map Widgets
ESRI Dev Meetup: Building Distributed JavaScript Map Widgets
Allan Glen
 
Architecture for scalable Angular applications
Architecture for scalable Angular applicationsArchitecture for scalable Angular applications
Architecture for scalable Angular applications
Paweł Żurowski
 
JSinSA - JS Visualisation APIs
JSinSA - JS Visualisation APIsJSinSA - JS Visualisation APIs
JSinSA - JS Visualisation APIs
Brendon McLean
 
Couchbas for dummies
Couchbas for dummiesCouchbas for dummies
Couchbas for dummies
Qureshi Tehmina
 
D3.js 30-minute intro
D3.js   30-minute introD3.js   30-minute intro
D3.js 30-minute intro
Felipe
 
Having fun with graphs, a short introduction to D3.js
Having fun with graphs, a short introduction to D3.jsHaving fun with graphs, a short introduction to D3.js
Having fun with graphs, a short introduction to D3.js
Michael Hackstein
 

Similar to Reactive data visualisations with Om (20)

Om nom nom nom
Om nom nom nomOm nom nom nom
Om nom nom nom
 
MongoDB for Time Series Data: Analyzing Time Series Data Using the Aggregatio...
MongoDB for Time Series Data: Analyzing Time Series Data Using the Aggregatio...MongoDB for Time Series Data: Analyzing Time Series Data Using the Aggregatio...
MongoDB for Time Series Data: Analyzing Time Series Data Using the Aggregatio...
 
Web components with java by Haijian Wang
Web components with java by Haijian WangWeb components with java by Haijian Wang
Web components with java by Haijian Wang
 
Svcc 2013-d3
Svcc 2013-d3Svcc 2013-d3
Svcc 2013-d3
 
SVCC 2013 D3.js Presentation (10/05/2013)
SVCC 2013 D3.js Presentation (10/05/2013)SVCC 2013 D3.js Presentation (10/05/2013)
SVCC 2013 D3.js Presentation (10/05/2013)
 
Big Data for each one of us
Big Data for each one of usBig Data for each one of us
Big Data for each one of us
 
PyCon SG x Jublia - Building a simple-to-use Database Management tool
PyCon SG x Jublia - Building a simple-to-use Database Management toolPyCon SG x Jublia - Building a simple-to-use Database Management tool
PyCon SG x Jublia - Building a simple-to-use Database Management tool
 
Visualization of Big Data in Web Apps
Visualization of Big Data in Web AppsVisualization of Big Data in Web Apps
Visualization of Big Data in Web Apps
 
Reactive programming every day
Reactive programming every dayReactive programming every day
Reactive programming every day
 
Monitoring Your ISP Using InfluxDB Cloud and Raspberry Pi
Monitoring Your ISP Using InfluxDB Cloud and Raspberry PiMonitoring Your ISP Using InfluxDB Cloud and Raspberry Pi
Monitoring Your ISP Using InfluxDB Cloud and Raspberry Pi
 
Compose Async with RxJS
Compose Async with RxJSCompose Async with RxJS
Compose Async with RxJS
 
SVGD3Angular2React
SVGD3Angular2ReactSVGD3Angular2React
SVGD3Angular2React
 
Interactively querying Google Analytics reports from R using ganalytics
Interactively querying Google Analytics reports from R using ganalyticsInteractively querying Google Analytics reports from R using ganalytics
Interactively querying Google Analytics reports from R using ganalytics
 
React table tutorial project setup, use table, and usefilter
React table tutorial project setup, use table, and usefilterReact table tutorial project setup, use table, and usefilter
React table tutorial project setup, use table, and usefilter
 
ESRI Dev Meetup: Building Distributed JavaScript Map Widgets
ESRI Dev Meetup: Building Distributed JavaScript Map WidgetsESRI Dev Meetup: Building Distributed JavaScript Map Widgets
ESRI Dev Meetup: Building Distributed JavaScript Map Widgets
 
Architecture for scalable Angular applications
Architecture for scalable Angular applicationsArchitecture for scalable Angular applications
Architecture for scalable Angular applications
 
JSinSA - JS Visualisation APIs
JSinSA - JS Visualisation APIsJSinSA - JS Visualisation APIs
JSinSA - JS Visualisation APIs
 
Couchbas for dummies
Couchbas for dummiesCouchbas for dummies
Couchbas for dummies
 
D3.js 30-minute intro
D3.js   30-minute introD3.js   30-minute intro
D3.js 30-minute intro
 
Having fun with graphs, a short introduction to D3.js
Having fun with graphs, a short introduction to D3.jsHaving fun with graphs, a short introduction to D3.js
Having fun with graphs, a short introduction to D3.js
 

Recently uploaded

“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
Edge AI and Vision Alliance
 
Nordic Marketo Engage User Group_June 13_ 2024.pptx
Nordic Marketo Engage User Group_June 13_ 2024.pptxNordic Marketo Engage User Group_June 13_ 2024.pptx
Nordic Marketo Engage User Group_June 13_ 2024.pptx
MichaelKnudsen27
 
Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)
Jakub Marek
 
Public CyberSecurity Awareness Presentation 2024.pptx
Public CyberSecurity Awareness Presentation 2024.pptxPublic CyberSecurity Awareness Presentation 2024.pptx
Public CyberSecurity Awareness Presentation 2024.pptx
marufrahmanstratejm
 
Trusted Execution Environment for Decentralized Process Mining
Trusted Execution Environment for Decentralized Process MiningTrusted Execution Environment for Decentralized Process Mining
Trusted Execution Environment for Decentralized Process Mining
LucaBarbaro3
 
AWS Cloud Cost Optimization Presentation.pptx
AWS Cloud Cost Optimization Presentation.pptxAWS Cloud Cost Optimization Presentation.pptx
AWS Cloud Cost Optimization Presentation.pptx
HarisZaheer8
 
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy SurveyTrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc
 
Dandelion Hashtable: beyond billion requests per second on a commodity server
Dandelion Hashtable: beyond billion requests per second on a commodity serverDandelion Hashtable: beyond billion requests per second on a commodity server
Dandelion Hashtable: beyond billion requests per second on a commodity server
Antonios Katsarakis
 
GraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracyGraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracy
Tomaz Bratanic
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
shyamraj55
 
System Design Case Study: Building a Scalable E-Commerce Platform - Hiike
System Design Case Study: Building a Scalable E-Commerce Platform - HiikeSystem Design Case Study: Building a Scalable E-Commerce Platform - Hiike
System Design Case Study: Building a Scalable E-Commerce Platform - Hiike
Hiike
 
Serial Arm Control in Real Time Presentation
Serial Arm Control in Real Time PresentationSerial Arm Control in Real Time Presentation
Serial Arm Control in Real Time Presentation
tolgahangng
 
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdfHow to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
Chart Kalyan
 
JavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green MasterplanJavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green Masterplan
Miro Wengner
 
Introduction of Cybersecurity with OSS at Code Europe 2024
Introduction of Cybersecurity with OSS  at Code Europe 2024Introduction of Cybersecurity with OSS  at Code Europe 2024
Introduction of Cybersecurity with OSS at Code Europe 2024
Hiroshi SHIBATA
 
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
Alex Pruden
 
5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides
DanBrown980551
 
Building Production Ready Search Pipelines with Spark and Milvus
Building Production Ready Search Pipelines with Spark and MilvusBuilding Production Ready Search Pipelines with Spark and Milvus
Building Production Ready Search Pipelines with Spark and Milvus
Zilliz
 
Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...
Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...
Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...
Jeffrey Haguewood
 
Taking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdfTaking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdf
ssuserfac0301
 

Recently uploaded (20)

“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
 
Nordic Marketo Engage User Group_June 13_ 2024.pptx
Nordic Marketo Engage User Group_June 13_ 2024.pptxNordic Marketo Engage User Group_June 13_ 2024.pptx
Nordic Marketo Engage User Group_June 13_ 2024.pptx
 
Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)
 
Public CyberSecurity Awareness Presentation 2024.pptx
Public CyberSecurity Awareness Presentation 2024.pptxPublic CyberSecurity Awareness Presentation 2024.pptx
Public CyberSecurity Awareness Presentation 2024.pptx
 
Trusted Execution Environment for Decentralized Process Mining
Trusted Execution Environment for Decentralized Process MiningTrusted Execution Environment for Decentralized Process Mining
Trusted Execution Environment for Decentralized Process Mining
 
AWS Cloud Cost Optimization Presentation.pptx
AWS Cloud Cost Optimization Presentation.pptxAWS Cloud Cost Optimization Presentation.pptx
AWS Cloud Cost Optimization Presentation.pptx
 
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy SurveyTrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy Survey
 
Dandelion Hashtable: beyond billion requests per second on a commodity server
Dandelion Hashtable: beyond billion requests per second on a commodity serverDandelion Hashtable: beyond billion requests per second on a commodity server
Dandelion Hashtable: beyond billion requests per second on a commodity server
 
GraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracyGraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracy
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
 
System Design Case Study: Building a Scalable E-Commerce Platform - Hiike
System Design Case Study: Building a Scalable E-Commerce Platform - HiikeSystem Design Case Study: Building a Scalable E-Commerce Platform - Hiike
System Design Case Study: Building a Scalable E-Commerce Platform - Hiike
 
Serial Arm Control in Real Time Presentation
Serial Arm Control in Real Time PresentationSerial Arm Control in Real Time Presentation
Serial Arm Control in Real Time Presentation
 
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdfHow to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
 
JavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green MasterplanJavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green Masterplan
 
Introduction of Cybersecurity with OSS at Code Europe 2024
Introduction of Cybersecurity with OSS  at Code Europe 2024Introduction of Cybersecurity with OSS  at Code Europe 2024
Introduction of Cybersecurity with OSS at Code Europe 2024
 
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
 
5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides
 
Building Production Ready Search Pipelines with Spark and Milvus
Building Production Ready Search Pipelines with Spark and MilvusBuilding Production Ready Search Pipelines with Spark and Milvus
Building Production Ready Search Pipelines with Spark and Milvus
 
Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...
Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...
Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...
 
Taking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdfTaking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdf
 

Reactive data visualisations with Om

  • 1. Reactive data visualisations with Om Anna Pawlicka Data Engineer @AnnaPawlicka Saturday, 28 June 14
  • 3. D3 (Data-Driven Documents) [to visualise data] • Data bound to DOM • Interactive - transformations driven by data • Huge community • Higher level libraries available Saturday, 28 June 14
  • 4. Leaflet.js & Dimple.js [higher level libraries] • Open-source Java-Script libraries • Interactive • Simple API • Access to underlying D3 functions Saturday, 28 June 14
  • 5. Facebook’s React [interface components] • Solves complex UI rendering • Declarative framework • No to “two-way data binding” • Re-renders the entire UI Saturday, 28 June 14
  • 6. U can’t touch this [a.k.a. Virtual DOM] • Developer describes the document tree • React : • Maintains virtual DOM • Diffs between previous and next renders of a UI • Less code • Shorter time to update Saturday, 28 June 14
  • 7. Om Nom Nom Nom [because we prefer Clojure] • Entire state of the UI in a single piece of data • Immutable data structures = Reference equality check • No need to worry about optimisation • Snapshottable • Free undo Saturday, 28 June 14
  • 8. Component life cycle protocols IWillMount IRenderState IShouldUpdateIInitState IRender Saturday, 28 June 14
  • 9. Liberator & core.async [component interaction] • Provide API to access external components (e.g. database): (defresource hello-world :available-media-types ["text/plain"] :allowed-methods [:get] :handle-ok (fn [_] "Hello, world.”)) • Send/receive messages between components using core.async channels: (let [ch (chan)] (go (while true (let [v (<! ch)] (prn "Vader: " v)))) (go (>! ch "No, I am your father") (<! (timeout 5000)) (>! ch "Search your feelings; you know it to be true!"))) Saturday, 28 June 14
  • 11. device_id | type | timestamp | value ------------------------------------------+------------------------+--------------------------------- 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:00:00+0000 | 8 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:05:00+0000 | 46 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:10:00+0000 | 23 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:15:00+0000 | 20 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:20:00+0000 | 67 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:25:00+0000 | 70 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:30:00+0000 | 10 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:35:00+0000 | 42 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:40:00+0000 | 95 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:45:00+0000 | 16 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:50:00+0000 | 79 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 00:55:00+0000 | 33 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:00:00+0000 | 45 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:05:00+0000 | 85 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:10:00+0000 | 32 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:15:00+0000 | 7 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:20:00+0000 | 92 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:25:00+0000 | 15 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:30:00+0000 | 9 8c077c2c3eac472d153886244e7b8aa6cad6a7e7 | electricityConsumption | 2014-01-01 01:35:00+0000 | 73 Saturday, 28 June 14
  • 12. Chart & API Saturday, 28 June 14
  • 13. (defresource measurements-resource [id type ctx] :allowed-methods #{:get} :available-media-types ["application/edn"] :handle-ok (partial retrieve-measurements id type)) (defresource devices-resource [_] :allowed-methods #{:get} :known-content-type? #{"application/edn"} :available-media-types #{"application/edn"} :handle-ok retrieve-devices) (defroutes app-routes (ANY "/devices/" [] devices-resource) (ANY "/device/:id/type/:type/measurements/" [id type] (measurements-resource id type)) (route/not-found "Not Found")) (def app (handler/site app-routes)) Saturday, 28 June 14
  • 14. (def app-model (atom {:devices {:all []} :chart {:data []}})) (om/root measurements-chart app-model {:target (.getElementById js/document "app") :shared {:url "http://localhost:3000/"}}) Saturday, 28 June 14
  • 15. (defn measurements-chart [cursor owner] (reify om/IInitState (init-state [_] {:chans {:event-chan (chan (sliding-buffer 1))}}) om/IRenderState (render-state [_ {:keys [chans]}] (dom/div nil (om/build device-form (:devices cursor) {:init-state chans}) (om/build chart/chart-figure (:chart cursor) {:init-state chans :opts {:event-fn get-measurements :chart {:div {:id "chart" :width "100%" :height 600} :bounds {:x "5%" :y "15%" :width "80%" :height "50%"} :x-axis "timestamp" :y-axis "value" :plot js/dimple.plot.line}}}))))) Initialise core.async channel Saturday, 28 June 14
  • 16. (defn measurements-chart [cursor owner] (reify om/IInitState (init-state [_] {:chans {:event-chan (chan (sliding-buffer 1))}}) om/IRenderState (render-state [_ {:keys [chans]}] (dom/div nil (om/build device-form (:devices cursor) {:init-state chans}) (om/build chart/chart-figure (:chart cursor) {:init-state chans :opts {:event-fn get-measurements :chart {:div {:id "chart" :width "100%" :height 600} :bounds {:x "5%" :y "15%" :width "80%" :height "50%"} :x-axis "timestamp" :y-axis "value" :plot js/dimple.plot.line}}}))))) This is how you construct components Triggered on arrival of a new message Saturday, 28 June 14
  • 17. (defn device-form [cursor owner] (reify om/IWillMount (will-mount [_] (let [host (:url (om/get-shared owner)) url (str host "devices/")] (GET url {:handler #(om/update! cursor [:all] %)}))) om/IRenderState (render-state [_ {:keys [event-chan]}] (let [devices (:all cursor)] (dom/div nil (dom/table nil (dom/thead nil (dom/tr nil (dom/th nil "Select") (dom/th nil "ID") (dom/th nil "Type") (dom/th nil "Description") (dom/th nil "Unit"))) (apply dom/tbody nil (om/build-all (form-row event-chan) devices)))))))) Sequence of components Saturday, 28 June 14
  • 18. (defn form-row [event-chan] (fn [the-item owner] (om/component (let [{:keys [id type description unit]} the-item] (dom/tr nil (dom/td nil (dom/input #js {:type "radio" :name "type" :value name :onChange (fn [e] (put! event-chan {:id id :type type}))})) (dom/td nil id) (dom/td nil type) (dom/td nil description) (dom/td nil unit)))))) Send message down the queue Saturday, 28 June 14
  • 19. (defn chart-figure [cursor owner {:keys [chart] :as opts}] (reify om/IWillMount (will-mount [_] (let [event-chan (om/get-state owner [:event-chan]) event-fn (:event-fn opts)] (go (while true (let [v (<! event-chan)] (event-fn cursor owner v)))))) om/IRender (render [_] (let [{:keys [id width height]} (:div chart)] (dom/div #js {:id id :width width :height height}))) om/IDidUpdate (did-update [_ _ _] (let [n (.getElementById js/document "chart")] (while (.hasChildNodes n) (.removeChild n (.-lastChild n)))) (when (:data cursor) (draw-chart cursor chart))))) Reads the message from the queue Saturday, 28 June 14
  • 20. (defn get-measurements [cursor owner message] (let [host (:url (om/get-shared owner)) {:keys [id type]} message url (str host "device/" id "/type/" type "/ measurements/")] (GET url {:handler #(om/update! cursor [:data] %)}))) Saturday, 28 June 14
  • 21. (defn draw-chart [cursor {:keys [div bounds x-axis y-axis plot]}] (let [{:keys [id width height]} div Chart (.-chart js/dimple) svg (.newSvg js/dimple (str "#" id) width height) data (get-in cursor [:data]) dimple-chart (.setBounds (Chart. svg) (:x bounds) (:y bounds) (:width bounds) (:height bounds)) x (.addCategoryAxis dimple-chart "x" x-axis) y (.addMeasureAxis dimple-chart "y" y-axis) s (.addSeries dimple-chart nil plot (clj->js [x y]))] (aset s "data" (clj->js data)) (.addLegend dimple-chart "5%" "10%" "20%" "10%" "right") (.draw dimple-chart))) Saturday, 28 June 14
  • 23. (def app-model (atom {:username-box {:username ""} :chart {:data []}})) (om/root lastfm-chart app-model {:target (.getElementById js/document "app") :shared {:api-root "http://ws.audioscrobbler.com/2.0/"}}) Saturday, 28 June 14
  • 24. (defn lastfm-chart [cursor owner] (reify om/IInitState (init-state [_] {:chans {:event-chan (chan (sliding-buffer 1))}}) om/IRenderState (render-state [_ {:keys [chans]}] (dom/div nil (dom/div #js {:className "container"} (dom/h3 nil "Last.fm chart") (om/build forms/input-box (:username-box cursor) {:init-state chans}) (dom/div #js {:className "well" :style #js {:width "100%" :height 600}} (om/build chart/chart-figure (:chart cursor) {:init-state chans :opts {:event-fn get-all-artists :chart {:div {:id "chart" :width "100%" :height 600} :bounds {:x "5%" :y "15%" :width "80%" :height "50%"} :x-axis "name" :y-axis "playcount" :plot js/dimple.plot.bar}}}))))))) Username input and chart components Saturday, 28 June 14
  • 25. (defn get-all-artists [cursor owner username] (let [api-root (:api-root (om/get-shared owner)) url (str api-root "?method=user.gettopartists&user=" username "&api_key=" api-key "&format=json")] (GET url {:handler #(om/update! cursor [:data] (get-in % ["topartists" "artist"]))}))) Saturday, 28 June 14
  • 26. (defn send-value [owner event-chan] (let [value (om/get-state owner :value)] (put! event-chan value))) (defn input-box [cursor owner] (reify om/IRenderState (render-state [_ {:keys [event-chan]}] (dom/div #js {:className "form-inline" :role "form"} (dom/div #js {:className "form-group"} (dom/input #js {:type "text" :className "form-control" :style #js {:width "100%"} :onChange (fn [e] (om/set-state! owner :value (.-value (.-target e)))) :onKeyPress (fn [e] (when (= (.-keyCode e) 13) (send-value owner event-chan)))})) (dom/button #js {:type "button" :className "btn btn-primary" :onClick (fn [e] (send-value owner event-chan)} "Go"))))) Saturday, 28 June 14
  • 28. Leaflet map & geocoding Saturday, 28 June 14
  • 29. (def app-model (atom {:map {:leaflet-map nil :map {:lat 50.06297958283694 :lng 19.94705200195313}} :panel {:coordinates nil}})) (om/root geocoded-map app-model {:target (. js/document (getElementById "app"))}) Saturday, 28 June 14
  • 30. (defn geocoded-map [cursor owner] (reify om/IInitState (init-state [_] {:chans {:event-chan (chan (sliding-buffer 1)) :pin-chan (chan (sliding-buffer 1))}}) om/IRenderState (render-state [_ {:keys [chans]}] (dom/div nil (om/build map-component (:map cursor) {:init-state chans}) (om/build panel-component (:panel cursor) {:init-state chans}))))) Saturday, 28 June 14
  • 31. (defn map-component [cursor owner] (reify om/IWillMount (will-mount [_] (let [event-chan (om/get-state owner [:event-chan])] (go (while true (let [v (<! event-chan)] (pan-to-postcode cursor owner v)))))) om/IRender (render [this] (dom/div #js {:id "map"})) om/IDidMount (did-mount [this] (let [node (om/get-node owner) {:keys [leaflet-map] :as map} (create-map (:map cursor) node) loc {:lng (get-in cursor [:map :lng]) :lat (get-in cursor [:map :lat])}] (.on leaflet-map "click" (fn [e] (let [latlng (.-latlng e)] (drop-pin owner leaflet-map latlng)))) (.panTo leaflet-map (clj->js loc)) (om/update! cursor :leaflet-map leaflet-map))))) Creates map and stores it in app state Saturday, 28 June 14
  • 32. (defn pan-to-postcode [cursor owner postcode] (let [postcode (.toUpperCase (string/replace postcode #"[s]+" "")) url (str geocoding-api-root postcode)] (GET url {:handler (fn [body] (let [map (:leaflet-map @cursor) {:keys [lat lng]} (location-from-response body)] (.panTo map (clj->js {:lat (js/parseFloat lat) :lng (js/parseFloat lng)}))))}))) (defn drop-pin [owner map latlng] (let [marker (-> (.addTo (.marker js/L (clj->js latlng)) map)) pin-chan (om/get-state owner [:pin-chan])] (put! pin-chan {:action :put :coordinates latlng}) (.on marker "click" (fn [e] (.removeLayer map marker) (put! pin-chan {:action :remove}))))) Saturday, 28 June 14
  • 33. (defn panel-component [cursor owner] (reify om/IWillMount (will-mount [_] (let [pin-chan (om/get-state owner [:pin-chan])] (go (while true (let [{:keys [action coordinates]} (<! pin-chan)] (if (= action :put) (om/update! cursor [:coordinates] coordinates) (om/update! cursor [:coordinates] nil))))))) om/IRender (render [_] (let [event-chan (om/get-state owner [:event-chan])] (dom/div #js {:id "panel"} (dom/h3 nil "Postcode lookup") (om/build forms/input-box cursor {:init-state {:event-chan event-chan}}) (om/build coordinates-component (:coordinates cursor))))))) Saturday, 28 June 14
  • 34. (defn coordinates-component [cursor owner] (om/component (dom/section nil (dom/h3 nil "Coordinates") (dom/p nil "(Click anywhere on a map)") (when cursor (dom/div nil (dom/label nil (str "Lat: " (.-lat cursor))) (dom/label nil (str "Lng: " (.-lng cursor)))))))) Saturday, 28 June 14
  • 35. Summary • You can leverage all of JavaScript and ClojureScript functionality and combine them with Om • Fast rendering and interactivity • Immutability = efficiency • Sane application structure • Reusability Saturday, 28 June 14