This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-01-07
Channels
- # announcements (27)
- # babashka (29)
- # beginners (280)
- # calva (34)
- # cider (4)
- # circleci (11)
- # cljfx (10)
- # cljsrn (1)
- # clojure (526)
- # clojure-dev (9)
- # clojure-europe (42)
- # clojure-finland (5)
- # clojure-losangeles (1)
- # clojure-nl (4)
- # clojure-taiwan (2)
- # clojure-uk (14)
- # clojurescript (40)
- # clr (43)
- # community-development (5)
- # css (4)
- # cursive (7)
- # duct (82)
- # events (1)
- # fulcro (72)
- # garden (44)
- # hoplon (3)
- # java (40)
- # jobs (4)
- # juxt (1)
- # luminus (53)
- # meander (13)
- # off-topic (26)
- # pathom (14)
- # pedestal (3)
- # portal (2)
- # rdf (3)
- # re-frame (54)
- # releases (10)
- # remote-jobs (3)
- # sci (74)
- # shadow-cljs (47)
- # startup-in-a-month (5)
- # testing (9)
- # tools-deps (73)
- # vim (12)
Dunno if this would come in useful, but I just spent a bit of time to get a rough translator going from fulcro rad attributes => malli-schema format. It will also figure out what entities you have (based of of ao/identities
) and generate the joins required. It has lots of rough edges, but it's good enough for my use-case of dogfooding some random initial state when I need something. I didn't use spec because it's nearly impossible to generate spec1 at runtime. A better solution might be to override defattr
to create a spec definition at compiletime, but I only thought of that after I wrote this 🙂.
https://gist.github.com/JJ-Atkinson/796d8e124edf684def2283dd9759515d

Trying to get the https://github.com/fulcrologic/fulcro-rad-demo to add an Account in SQL configuration. Everything works fine till I try to add an account, then I get the error
originalMessage: "Data conversion error converting \"OTHER to ENUM\""
Attached is the complete error listing. What would be a good way to fix this?
I think that the issue is in the https://github.com/fulcrologic/fulcro-rad-sql/blob/develop/src/main/com/fulcrologic/rad/database_adapters/sql/resolvers.clj. It may be possible to wrap enum into as-other
as can be seen in the https://cljdoc.org/d/seancorfield/next.jdbc/1.1.613/doc/getting-started/tips-tricks#working-with-enumerated-types. Looking for advice on how to fix this.The SQL plugin is very alpha. Enumerations, in particular, are known not to work well. I’m actively interested in finding a maintainer, and currently don’t have time to work on it.
@tony.kay understood, I just wanted to throw the question out there, in case someone would recommend a good solution that I can start with.
@tony.kay also sorry about the bad pull request. I started the pull request right after i committed just the addition to the require block, I thought that github would snapshot the commit, but then I pushed two more commits. After that I pressed complete pull request button and the two later commits ended up being part of it. I feel bad about it because I do not like to waste peoples time going through unnecessary code.
no worries. The solution could be “find standard SQL that works for all dbs and use that in the plugin” or “write a protocol for the diffs, and use that to access the dbs from the adapter”. Requires some R&D
@tony.kay What is this line in fulcro.components for?
#?(:clj
(defn -legal-keys
"PRIVATE. Find the legal keys in a query. NOTE: This is at compile time, so the get-query calls are still embedded (thus cannot
use the AST)"
[query]
(letfn [(keeper [ele]
(cond
(list? ele) (recur (first ele)) <- THIS ONE ================================================================================
(keyword? ele) ele
(is-link? ele) (first ele)
(and (map? ele) (keyword? (ffirst ele))) (ffirst ele)
(and (map? ele) (is-link? (ffirst ele))) (first (ffirst ele))
:else nil))]
(set (keep keeper query)))))
https://github.com/fulcrologic/fulcro/blob/ee9545df41795d1acd5365c1926036740a5da98e/src/main/com/fulcrologic/fulcro/components.cljc#L1311
I am trying to figure out, if/how I can use parameters directly in queries. Like:
{:query [:comment/author (:comment/body {:truncated? true})]}
I was hoping that when a query is made to the client-side database, the parameter will simply be ignored, but the query will still be sent to the backend unchanged.It’s used to generate compile-time errors around defsc to prevent common errors. Parameters don’t make sense in static component queries, and the generalized load support allows you to add them when you need them. This makes the use of dynamic queries that add them also largely irrelevant. Fulcro could support that, but I have no intention of doing so at the moment. The earlier versions technically did support this, but no one seemed to use it, including me (and not a single complaint from removal).
It was a source of complexity that I chose not to move forward with, given the amount of complexity it added to everything. I judged it “incidental”. I’m not dead-set on that judgement “forever”, but until I see really compelling use-cases that cannot be solved (reasonably well) with the existing APIs I have no interest in making things more complex based on the occasional “preference” to embed them that way.
My recommendation is to use namespaced keys in the params
argument of load, move those into your server parsing env (so they propagate to children). The namespacing allows for a form of “targeting” of the query params. Granted that solution is not perfect, but in the real world with the real features of load, it has solved every case I’ve needed very easily.
Sorry, you asked about the specific line. Yes, that line is there to deal with the fact that one could technically embed parameters in queries.
When I talk about the query parameter stuff above, I mean that you used to be able to do this:
[(:prop {:start ?start})]
and then later call set-query
to bind ?start
to something. That’s the parameter support I dropped.
you can still call set-query
and put parameters in the query of a component. It could technically have some bugs (I do not imagine it does), because no one uses it to my knowledge, but try it if you wish.
I am trying to.
When writing (:comment/body {:truncated? true})
, like above, I just get an Invalid expression
error from cljs.
When I quote the expression, I get was destructured in props, but does not appear in the :query
I am trying to figure out how to fix this for me.
It doesn’t matter so much whether it works or not. It’s not really worth my time right now, but curiosity grabbed me.
I saw the specific line and though: “Great, I am not the first one to try this” 🙂
right, I don’t have tests covering the case for query params in component static queries but there is some legacy bit here and there that made it through the removal. Given the bug, then only way to do it right now is via set-query!
My general reasoning was: You need to pre-set bindings and parameters before they make sense, so why not just use set-query?
but because all that introduces the need for logic in the same code points as load, why not just use load params?
The answer, of course, being you want the params in a particular spot. My workaround mentioned before works quite well for me, but you still have set-query!
if you really really need it.
@tony.kay While I am already at it: Here are two questions that I thought about and would love your opinion whether these things make sense or not. I am aware that these things are just “sugar” and not a necessity. 1. How would I write a function for something like an … “anonymous component” for query purposes? I often have to write components just for the query:
(defsc Child [_ _]
{:query [:id]
:ident :id})
(defsc Something [_ {:keys [children]}]
{:query [{:children (comp/get-query Child)}]}
(str "I have " (count children) " children!")
)
; to
(defsc Something [_ {:keys [children]}]
{:query [{:children (comp/sc [:id] {:ident :id})]} ; <- something like this ; or (comp/sc {:query [:id] :ident :id})
(str "I have " (count children) " children!")
)
2. Would is make sense to allow idents of length one for singletons?
(defsc NavDrawer [_ _]
{:query [:open?]
:ident (fn [] [:component :nav-drawer])
(defsc Root [_ _]
{:query [{:nav-drawer (comp/get-query NavDrawer)}]})
; The db:
{:nav-drawer [:component :nav-drawer] ; This seems redundant for singletons
:component {:nav-drawer {:open? false}}
;;; instead NavDrawer with a single item ident
(defsc NavDrawer [_ _]
{:query [:open?]
:ident (fn [] [:nav-drawer]))
; would lead to this db:
{:nav-drawer {:open? false}}
1. Technically you just need to add the metadata that get-query
adds, and have the :component in that have a component options ident (which in cljs is just an js object with some special fields). The only thing special about it is the component used for normalization; however, I do not see why you’d want that, because you should be doing the rendering in the child, and therefore should be writing a child anyway. Writing a separate component just for a query is a very rare thing indeed (your example is a contrived use-case).
2. you can certainly write a macro that transforms that, but I don’t want to add yet another syntax sugar onto a macro that already has so many little things like that. On the one hand it’s “par for the course”, but on the other what “table name” am I to auto-select for you? Also, since keywords without a vector mean :x
=> [:x (:x props)]
having another special variant [:x]
=> [:component/id :x]
along with [:x :y]
=> [:x (:y props)]
or (fn [] literal-ident)
is adding to a thing that is already a bit excessive, and doesn’t even follow the “sugar pattern” of pulling something from props to generate the ident.
To 2.: Is it really another case? For singletons a table-name doesn’t make sense.
I thought not about a keyword without a vector, but a vector with a single entry as a literal-ident (fn [] [:x])
Like you could for a normal map in clojure:
(get-in state [:nav-drawer])
and
(assoc-in state [:nav-drawer])
I see what you’re getting at. I definitely do not want to introduce that complication. It would affect a lot of things for no tangible benefit.
(state would then have to be able to contain card 1 vectors as idents, which would confuse all manner of things)
Hm. I thought that could be as simple as remove any checks if an ident is exactly card. 2. Ok. Thanks for your reply.
A more solid use-case for (1) is (df/load! :kw (sc ident query))
where you want normalization of some data into the client that doesn’t have UI of that shpe
See configure-component!
for ideas on making the query thing, but be careful: the component registry is normally part of the story, and your use-case probably doesn’t want to register, so you don’t really want all the mess that configure component does.
To test if your thing works, just make sure get-query and get-ident (both arities) work on it.
Is it possible to watch changes in props of a child component? I want a parent component to be able to see if a child (form) component is valid (I’m using the non-spec validators)
The trick is that if you use an optimized call the re-renders just the child, then you need to make sure the parent is refreshed (usually by using something like :only-refresh
. Depends on renderer. Oh, in F2, which I think you’re using, you’d include a parent prop in the follow-on read
ah I see.. so on any updates to the child just make sure those transactions are also updating the parent ident
in F3 I figured out that the targeted refresh is usally not needed for performance (if you do other things right), so default is to render from root, which in turn makes doing that a non-issue…you don’t even have to think about it. It pushes the potential complexity off to an optimization step instead of troubling you with it from the outset as a pre-optimization
Speaking of validation, on initial load my fields are “unchecked” but once I edit one of them, the rest of them turn invalid. Is there a way around this? I’d rather not show validation messages until the user has tried to modify that field
I see.. so what is the recommendation in this scenario? The onChange is causing the whole form to be invalid at this point
Does anyone has an example with two nested dynamic-routers, where both routers have a parameter in their route-segment? I am stuck with initializing the state correctly.
[„decision“ :process/slug]
And
[„proposal“ :proposal/id]
or even [„proposals“]
Thinking about it. I have problem placing another dynamic router in a target with a parameter. The second parameter isn‘t even needed.
should nest fine, but you need to make sure you pass a complete route to it…i.e. the vector of a route MUST have 4 elements in order to match
if you fail to specify a bit of the segment the routing will stop at that point and assume the sub-routes are “right” already
But how do I initialise the inner router?
It can‘t be initialised on app startup because of the parameter.
sure it can, you can initialize state and set root with set-root!
, then use dr/route-to!
I use a “ready” flag on the root node to prevent rendering until things have settled (i.e. a ok-result handler on loading app config or something)
Ok something is kind of working now…
Having the situation, where I have two deferred routings with the same ident
RootRouter
routes to Process
.
and the
ProcessRouter
(inside Process
) routes to ProcessHome
.
Process
offers UI for every sub page of the router. [:process/slug :process/title]
ProcessHome
has details for the Process. [:process/slug :process/title :process/description]
@tony.kay Hm. Now I have noticed that I will often have the case that the children have the same ident as their parents. 😕 Do you have a pointer how to avoid this messing with my routing? Or nested routers in general? I have to admit that I’m pretty lost in figuring out how to do it “right”.
What are you doing that is causing your children to have the same idents as the parent?
especially as routes? It seems like in a path once you’ve identified a thing, that the children of that thing would not be the thing
Ok. I stripped down my case to the following code. I think writing it down like this is easier than me trying to explain it to you.
(defsc ProposalList [_ _]
{:query [::process/slug {::process/proposals (comp/get-query Proposal)}]
:ident ::process/slug
:route-segment ["proposals"]}
(button {:onClick open-add-proposal-modal!} "Add a new proposal to this list!"))
(defsc ProcessHome [_ _]
{:query [::process/slug ::process/title ::process/description]
:ident ::process/slug
:route-segment ["home"]}
(button {:onClick open-add-proposal-modal!} "Start with a new proposal!"))
(defrouter ProcessRouter [_ _]
{:router-targets [ProcessHome ProposalList]})
(def ui-process-router (comp/factory ProcessRouter))
(defsc Process [_ {:ui/keys [process-router new-proposal-modal]}]
{:query [::process/slug
{:ui/process-router (comp/get-query ProcessRouter)}
{:ui/new-proposal-modal (comp/get-query ProposalModal)}]
:ident ::process/slug
:route-segment ["decision" ::process/slug]}
(comp/fragment
(dom/a {:href (str "/decision/" slug "/home")} "Back to home!")
(ui-global-add-proposal-to-process-modal new-proposal-modal)
(ui-process-router process-router)))
I change-route!
based on the URL, when it changes. I extract all params from the route-segment
s and add them as extra-params to change-route!
. For example: The site navigates to /decision/my-test-decision/proposals
, I call (dr/change-route! app ["decision" "my-test-decision" "proposals"] {::process/slug "my-test-decision"})
. This way the router targets in ProcessRouter
get the slug as well.I would call is something different…just because you need to know something about your children does not mean you are your children
What exactly do you mean with “Use the info out of the child to know your slug.“?
Notice, that I shortened the query for Process
and it could query for ::process/title
as well.
In my case even the ui-global-add-proposal-to-process-modal
has the ident: ::process/slug
because it needs access to ::process/all-proposals
(As a proposal can have another proposal as its parent) and ::process/slug
Your choices are: • Reorganize things so you don’t end up with deferred routers on the same ident. • Don’t use dynamic routers.
“has the ident: `::process/slug` because it needs access to `::process/all-proposals` ” is an invented constraint by you not Fulcro or Pathom
“I shortened the query for `Process` and it could query for `::process/title` ” But IS it a Process? It has to, as a parent, know about children. composition recommendations are that children are unaware of parents. In a UI parents are very very often forced to have knowledge of their children. You mix it into the query and render body. You’re stuck with it.