how do you handle multiple controllers btw @metametadata ?
if a component dispatches signals and the intended controller may be any one of N.. what do you do? Does a "parent" controller need to know the "child" controllers?
Hey @kauko! Could you please give an example of "multiple controllers"? Because, stricly speaking, for each app there's only a single controller.
Maybe you mean how one can define this controller using more smaller ones (i.e. like redux does with reducers)?
Maybe
Just need some way to split the controller into multiple files
otherwise any app of non-trivial size will have a 2k+ loc controller 😛
(well, maybe not, but splitting is a must IMO)
Yeah, I agree. It's something I thought about but didn't prescribe in the framework yet.
There could be different solutions. On top of my head: - define controller as multimethod, this way you can scatter signal processing across many files easily; but there may be problems with pattern matching now?.. - "Flux way": ensemble a controller from smaller controllers; when a "parent" receives a signal it sends the signals to all the children; this is also how Redux works if I'm not mistaken; - the "chain of responsibility"-kinda approach: ensemble controller from the smaller controllers; when it receives a signal it first sends it to the first child, if it wasn't handled - sends it to the second child, etc. until someone is able to handle the signal
I recall I liked the last approach the most
because in this case you can easily throw an exception if no handler was found for a signal. Redux in such situations silently ignores the error.
Just in case, here's the Redux pattern explained by author: https://stackoverflow.com/questions/35667775/state-in-redux-react-app-has-a-property-with-the-name-of-the-reducer/35674297#35674297
And here's a snippet for my liked approach:
;; spec
; only one controller will handle a signal
:control (ui/either #{app-control
router/control
files-logic/control
settings-logic/control})
(ns frontend.settings.logic
(:require [cljs.core.match :refer-macros [match]]))
(defn control
[model signal dispatch-signal dispatch-action]
(match signal
[:on-select-theme id] (dispatch-action [:select-theme id])
; ...
:else :bypass)) ; <-------------------------------------------- let other controller handle the unknown signal
(defn either
"Returns a function of any args which calls functions one by one while :bypass is returned."
[fns]
(fn either-fn [& args]
(loop [f (first fns)
next-fns (rest fns)]
(when (nil? f)
(throw (ex-info (str "No function was able to handle passed args: " (pr-str args))
{})))
(let [result (apply f args)]
(if (= result :bypass)
(recur (first next-fns) (rest next-fns))
result)))))
Yeah, I think that could work. It's important that the ability to give components custom controllers is retained, for testing for example. So avoid some register-controller
functions that add a controller to some global thingy
and when I work on my view component and add some new signals, I want to add it to one place, not several places
Sounds reasonable!
this either
function can be used to split a reconciler as well