SlideShare uma empresa Scribd logo
1 de 36
Baixar para ler offline
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

Mais conteúdo relacionado

Mais procurados

A Computational View of MapReduce
A Computational View of MapReduceA Computational View of MapReduce
A Computational View of MapReduceZilong 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 FS2Luis 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 SkywritingDerek Murray
 
rx.js make async programming simpler
rx.js make async programming simplerrx.js make async programming simpler
rx.js make async programming simplerAlexander 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 LTYuumi Yoshida
 
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 PiPance 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 RXJSRyan 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 AnalyticsDave 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.v1Bitla Software
 
Bindings: the zen of montage
Bindings: the zen of montageBindings: the zen of montage
Bindings: the zen of montageKris Kowal
 
Deep learning study 3
Deep learning study 3Deep learning study 3
Deep learning study 3San 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 31Mahmoud Samir Fayed
 
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 196Mahmoud Samir Fayed
 

Mais procurados (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
 

Destaque

Optimizing AngularJS Application
Optimizing AngularJS ApplicationOptimizing AngularJS Application
Optimizing AngularJS ApplicationMd. Ziaul Haq
 
Habits of Effective Designers
Habits of Effective DesignersHabits of Effective Designers
Habits of Effective DesignersDUSPviz
 
Habits of Effective Designers - Handout
Habits of Effective Designers - HandoutHabits of Effective Designers - Handout
Habits of Effective Designers - HandoutDUSPviz
 
Art of-presentations
Art of-presentationsArt of-presentations
Art of-presentationsDUSPviz
 
Mapping with Adobe CC
Mapping with Adobe CCMapping with Adobe CC
Mapping with Adobe CCDUSPviz
 
Intro to Adobe Illustrator
Intro to Adobe IllustratorIntro to Adobe Illustrator
Intro to Adobe IllustratorDUSPviz
 

Destaque (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
 

Semelhante a Reactive data visualisations with Om

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 WangGWTcon
 
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 usOSCON 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 toolCrea 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 AppsEPAM
 
Reactive programming every day
Reactive programming every dayReactive programming every day
Reactive programming every dayVadym 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 PiInfluxData
 
Compose Async with RxJS
Compose Async with RxJSCompose Async with RxJS
Compose Async with RxJSKyung Yeol Kim
 
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 ganalyticsJohann 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 usefilterKaty 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 WidgetsAllan Glen
 
Architecture for scalable Angular applications
Architecture for scalable Angular applicationsArchitecture for scalable Angular applications
Architecture for scalable Angular applicationsPaweł Żurowski
 
JSinSA - JS Visualisation APIs
JSinSA - JS Visualisation APIsJSinSA - JS Visualisation APIs
JSinSA - JS Visualisation APIsBrendon McLean
 
D3.js 30-minute intro
D3.js   30-minute introD3.js   30-minute intro
D3.js 30-minute introFelipe
 
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.jsMichael Hackstein
 

Semelhante a 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
 

Último

TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?Antenna Manufacturer Coco
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024The Digital Insurer
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfhans926745
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
Evaluating the top large language models.pdf
Evaluating the top large language models.pdfEvaluating the top large language models.pdf
Evaluating the top large language models.pdfChristopherTHyatt
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 

Último (20)

TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Evaluating the top large language models.pdf
Evaluating the top large language models.pdfEvaluating the top large language models.pdf
Evaluating the top large language models.pdf
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 

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