using core.async / ajax data in om component

841 views Asked by At

I am currently experimenting with om and try to load external data for displaying it in a component.

My detail component:

(defn detail-component [app owner opts]
  (reify
    om/IInitState
    (init-state [_]
                (om/transact! app [:data] (fn [] "Test")))
    om/IWillMount
    (will-mount [_]
      (go (let [foo (<! (fetch-something 1))]
           (om/update! app #(assoc % :data foo))
           )))
    om/IRender
    (render [_]
      (dom/div nil
               (dom/h1 nil "Foo")
               (om/build another-component app)
               )
      ))
  )

(fetch-something is retrieving data from an API).

(defn another-component [{:keys [data]}]
  (om/component
    (.log js/console data)
    (dom/h2 nil "Another component goes here")
    (dom/h2 nil (data :description))
    )
  )

So to summarize, detail-component fetches data before mounting, attaches it to app and builds another-component. another-component then takes the description out of that data and displays it.

However when executing, I am getting Uncaught TypeError: Cannot read property 'call' of null at the point where I am trying to access the description. This indicates to me that at the point when another-component is getting built, the data is not there yet and it fails.

How can I tell om to build the app when data is available? Or do I have to build in some nil? checks?

1

There are 1 answers

4
edbond On BEST ANSWER

Working example using state:

project.clj

(defproject asajax "0.0.1-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License - v 1.0"
            :url "http://www.eclipse.org/legal/epl-v10.html"
            :distribution :repo}
  :min-lein-version "2.3.4"
  :source-paths ["src/clj" "src/cljs"]
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [org.clojure/clojurescript "0.0-2371"]
                 [org.clojure/core.async "0.1.267.0-0d7780-alpha"]
                 [om "0.7.3"]
                 [com.facebook/react "0.11.2"]]
  :plugins [[lein-cljsbuild "1.0.4-SNAPSHOT"]]
  :hooks [leiningen.cljsbuild]
  :cljsbuild
  {:builds {:asajax
            {:source-paths ["src/cljs"]
             :compiler
             {:output-to "dev-resources/public/js/asajax.js"
              :optimizations :whitespace
              :pretty-print true}}}})

core.cljs

(ns asajax.core
  (:require [om.core :as om :include-macros true]
            [om.dom :as dom :include-macros true]
            [cljs.core.async :as async])
  (:require-macros [cljs.core.async.macros :refer (go)]))

(enable-console-print!)

(def app-state (atom {}))

(defn another-component [{:keys [data]}]
  (reify
    om/IRenderState
    (render-state [_ state]
      (dom/div nil
               ;; (.log js/console data)
               (dom/h2 nil "Another component goes here")
               (dom/h2 nil (:description state))))))

(defn fetch-something [x]
  (let [c (async/chan)]
    (go
      ;; pretend a blocking call
      ;; wait for 2 sec
      (<! (async/timeout 2000))
      (>! c {:data "Roast peach & Parma ham salad"
             :description "This is a lovely light starter with fantastic sweet, salty and creamy flavours"}))
    c))

(defn detail-component [app owner opts]
  (reify
    om/IInitState
    (init-state [_]
      {:data "Test"})
    om/IWillMount
    (will-mount [_]
      (go (let [foo (<! (fetch-something 1))]
            ;; (prn "GOT" foo)
            (om/set-state! owner foo))))
    om/IRenderState
    (render-state [_ state]
      (dom/div nil
               (dom/h1 nil (:data state))
               (om/build another-component app
                         {:state (om/get-state owner)})))))

(om/root
 detail-component
 app-state
 {:target (. js/document (getElementById "app"))})

UPDATE2

Here is one using sablono and not using set-state!

Add to project.clj

         [sablono "0.2.22"]

Full core.cljs

(ns asajax.core
  (:require [om.core :as om :include-macros true]
            [om.dom :as dom :include-macros true]
            [cljs.core.async :as async]
            [sablono.core :as html :refer-macros [html]])
  (:require-macros [cljs.core.async.macros :refer (go)]))

(enable-console-print!)

(def app-state (atom {:data "Initial"
                      :description "Loading..."}))

(defn another-component [{:keys [description]}]
  (om/component
   (html
    [:.description
     [:h2 "Another component"]
     [:h2 description]])))

(defn fetch-something [x]
  (let [c (async/chan)]
    (go
      ;; pretend a blocking call
      ;; wait for 2 sec
      (<! (async/timeout 2000))
      (>! c {:data "Roast peach & Parma ham salad"
             :description "This is a lovely light starter with fantastic sweet, salty and creamy flavours"}))
    c))

(defn detail-component [app owner opts]
  (reify
    om/IWillMount
    (will-mount [_]
      (go (let [foo (<! (fetch-something 1))]
            ;; (prn "GOT" foo)
            (om/update! app foo))))
    om/IRender
    (render [_]
      (html [:div
             [:h1 (:data app)]
             (om/build another-component app)
             ]))))

(om/root
 detail-component
 app-state
 {:target (. js/document (getElementById "app"))})