4

I'm struggling with a circular dependency in ClojureScript. I'm trying out this language for a month, haven't ever worked with the real thing (Clojure).

I have a client side app that uses secretary as a router. When I'm defining my routes, they're handler functions, push values to the history-channel, which is then consumed by the main app component that displays particular views. Thus, the values I push from my routes, contain a reference to the view function. This view function are om components that render the given location. In these view functions, I often need to generate links, URLs to other locations in the app. These URLs are generated from the same handler functions that references them. That's how my circular dependency is born. What is an elegant way to resolve it?

router -> views -> router

-- route.cljs

(ns myapp.route
  (:require [secretary.core :as secretary :include-macros true :refer [defroute]]
            [myapp.views.welcome :as welcome]
            [myapp.views.some :as some]))

(defroute home "/" {}
  (put! history-chan {:token "/"
                      :view welcome/view}))

(defroute some "/some" {}
  (put! history-chan {:token "/some"
                      :view some/view}))

-- welcome.cljs

(ns myapp.views.welcome
  (:require [om.core :as om :include-macros true]
            [sablono.core :as html :refer-macros [html]]
            [myapp.route :as route]))

(defn view [state owner]
  (reify
    om/IRender
    (render [_]
      (html [:div [:a {:href (route/some)}]]))))
4
  • 1
    move everything to one file :-) Commented May 19, 2014 at 12:06
  • Unfortunately there are no easy answers to circular dependencies in Clojure. Your best bet is to refactor, rewrite your code to remove the dependency in one direction. Commented May 19, 2014 at 14:35
  • Thanks @ErikKronberg I'd appreciate if you'd give an example on how to refactor this particular case. That would be very helpful. Commented May 19, 2014 at 14:45
  • I wonder if a reverse URL function (e.g. as in Django) might be a simple solution? Commented Jul 21, 2014 at 7:46

2 Answers 2

2

Circular dependencies in Clojure have no easy or elegant solutions. Most likely you have to restructure your code. You'll have to mess around with it to find something you like. Just off the top of my head I might do something like this:

-- route.cljs

(ns myapp.route
  (:require [secretary.core :as secretary :include-macros true :refer [defroute]]
            [myapp.views.welcome :as welcome]
            [myapp.views.some :as some]))

(defroute home "/" {}
  (welcome/route))

(defroute some "/some" {}
  (put! history-chan {:token "/some"
                      :view some/view}))

-- welcome.cljs

(ns myapp.views.welcome
  (:require [om.core :as om :include-macros true]
            [sablono.core :as html :refer-macros [html]]))

(declare view)

(defn route []
  (put! history-chan {:token "/"
                      :view view}))

(defn view [state owner]
  (reify
    om/IRender
    (render [_]
      (html [:div [:a {:href (route)}]]))))

That's just one possible solution.

Sign up to request clarification or add additional context in comments.

1 Comment

Not bad Erik, not bad. However, the trick lies in the defroute macro. With secretary I have to call the generated named function. I can't simply call route, it would not give me the token, it would simply call what secretary calls when the location changes, ie. put!.
0

I had such problem, I don't use route/some to generate urls.

Router (history.cljs) include all views:

        [react.nav :as nav]
        [react.loading :as loading]
        [react.alerts :as alerts]
........
(def history (History.))

(events/listen
 history (.-NAVIGATE goog.history.EventType)
 (fn [e]
   (secretary/dispatch! (.-token e))))

(defroute "alerts" {:as params}
  (nav/switch-to "Alerts")
  (alerts/display))

Views doesn't require router. Links works like this:

(def history (History.))
   .....
          :on-click (fn [e]
            (.preventDefault e)
            (.setToken history link))}

You can call setToken with any link you need: (str "alerts/" sort-key "/" filter-str)

HTH

1 Comment

Right, but I don't want to duplicate the string when calling setToken, I want secretary to generate it. DRY: when I change the route in defroute, I don't have to go through views and guess (grep) where I used that string.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.