Fork me on GitHub

@tony.kay I followed the new UI routing guide today; it was clear and easy to understand. Thanks! :thumbsup:


Do I understand correctly that in a defsc, this should give me a reference to the dom node? :componentDidMount (fn [] (js/console.log (fulcro.client.dom/node this))) Because all I’m seeing is undefined


@pithyless you can try (js/ReactDOM.findDOMNode this), I think that works


@wilkerlucio weird; ok, so both dom/node and findDOMNode work; but the problem was my root node was a material-ui-next component I was mounting. When I switched to a plain dom/div everything worked fine.


For reference, I’m using the components via:

(defn react-factory [cls]
  (fn [props & children]
    (apply react/createElement cls props children)))


a good recommendation is to use ref to point to things


(my-element {:ref #(gobj/set this "element" %)})


Is :ref a react ref?


so, dom stuff you need to use #js {:ref ...} and on factory things {:react-ref ...}


this way you can be sure you are targeting the correct thing


good to know, thanks


Hi, I am trying to figure out routing. I based my setup on the Routing Video and the corresponding code and the chapters in the book. My goal is to have a route like with a list of postal addresses that can be added to. Showing them works fine, but adding one via an input field clears all data from the ui. The actual processing of the added address works fine, tempid, server and all. In the example there are static pages which get routet to from the routing tree as [:home-page :single]. Then there are dynamic pages that get routet to as [:report/by-id :param/id]. The former shows up in the db root. The latter does not, since it uses the normalized entities. When I put an input field at the top of the list on one of the static routes to add an item it works and the ui updates. In my case that is the person list. When I put an input field at the top of a dynamic page adding an item adds it to the db, the server with the tempid and all, but the page looses its data. In my case that is the person page with the addresses. On the person/20 page there should be some information on the person and some addresses. Does this really call for two nested routers? But when I try to do it with one router, how do I pass the :param/id from the url to the router. When I put in the static keyword for the pages all routes work but the :param/id is not passed. When I put in the :param/id I do not get the router to work, except when nested as in the example. How do I build a TopRouter that can pass :param/id to one of the otherwise static pages?


@magra Not exactly sure I understand everything. But have you tried just adding the url params where you need them by mutation ?


(def routing-tree
--- more top-router routes. ---
   ((r/make-route :person-page [(r/router-instruction :top-router [:person-page :top])
                                (r/router-instruction :person-router [:person/by-id :param/id])])))
In the person router I can use the :param/id. If I try to use only one router, where would that go in the :top-router?


I need the :top and the :param/id how would I do both on the same level?


@U3LP7DWPR are you suggesting to put them someplace else?


@pithyless how do you like material-ui-next - are you pulling in via shadow-cljs or cljsjs? I believe has an issue to pull in material-ui-next.


@donmullen so far so good, but I have not yet tried to do something crazy where my customizations will break everything. ;] I am running the latest 1.0.0-beta.34 via shadow-cljs (which has been a joy to work with) and have integrated several react-virtualized based components without a problem. The only issues I’ve seen so far are breaking changes (read the changelog before bumping versions) and partially integrated ThemeProvider (so you have to sometimes set custom styles for components, where you would think it should just pick them up).


@magra Frustrating...I don't see anything off-hand


any console errors?


ident could be the problem if your router's ident function isn't consistent with the component idents


Refresh of a component works as follows: The component you transact on is queued for refresh. It's ident is used to create a query for JUST that component [{[ident of component] [query of component]}], and then Fulcro refreshes just that component (subtree). If the ident is wrong, then this query returns empty data, which will cause the component to clear


Oh, and that is your problem


you're using :id instead of :db/id in your ident


@tony.kay Wow! Thanx! That solved it. I did that because I confused it with param/id.


"Refresh going blank" is almost certainly an ident problem every time 🙂


I need to add something in that gives an additional warning at least in dev mode


@magra The defsc macro is build to help warn you of that, but you "fixed the warning" by adding :id to the query instead of fixing the ident 😜


on reflection I realized I did give you a warning for that case: if your query doesn't ask for something in the ident...but if you fix it by asking for the "wrong thing", then it doesn't help 🙂


Your query says [:id :db/id ...]


seems to me when you messed up the ident it told you :id wasn't in your query, but was used in the ident...I'm guessing your "fix" was to add it to the query


Perhaps I can clarify the error message


@tony.kay on that, when I was trying to write a union component using defsc, the error message about the query was confusing, it just said something like unexpected [], I had to spend a good time to figure I had to make my :query a fn to use union queries there


Yes, that should be improved too


the docs say it, but error message should be better


@tony.kay I have the id plus :db/id in the query because I am still not to clear where the :param/id that bidi passes into the routing tree becomes the :db/id in the component called by the router.


So, :param/id is just a placeholder that is replaced by the routing instructions based on the values in the routing-parameters map. Nothing at all to do with query or component


Did you read the updated routing section in dev guide? It’s much clearer now


I have and thank you for the work you put not only into the libraries but also into the documentation. I will have to work through that a few more times 😉


You’re welcome.


When I pass data to a defcard-fulcro does it need to be denormalized or will it automatically denormalized?


how are you passing it?


(defcard-fulcro card-name Root {})


As the initial state, yes.


normalized. Devcards puts that in an atom, and when Fulcro gets an atom it wants it normalized…why not using initialstate of the Root , though?


The card will auto-pull :initial-state from Root if that map is empty


I wanted to test the root component with different states. Maybe I’m doint something stupid


so, you can do that…just generate a tree, then run through through tree->db


or hand-generate normalized…but that’s tedious


If you want to test with different state for UI verification, use a static normal defcard


that way you’re not making a whole app…you’re just passing a single set of props.


Initial state is really something that is a starting point…the started callback is where you would typically respond to the environment and modify how the app behaves once started


e.g. via loads, merge-component!, etc.


When I use a static defcard, I cannot use mutations, right?


correct, but initial state is just initial state


so I thought you were wanting to just “see” what it looked like


static card is great for that


But I think I get it, for static, a simple defcard is the easiest


So, how do you plan to trigger these different initial states?


in the real world


Yes, in the real world the come from the app state / queries. I wonder what is the advantage of defcard-fulcro over having the application running then


Maybe a more concrete example would help? For a “person list” I want a defcard for the empty list, so I can see, and test the behaviour of adding a new person. I want also a defcard for a “person list” that is already populated with data. It’s well possible I’m doing something unusual or silly.


When I have a bigger application that has many of these components, person list, a organization list and other components, I thought it might be usefull to be able to test each component in isolation but including the mutations.


Sure, that is very common. Use the started callback, though


have an initial state that is the “true” initial state. Then in each card run transations and state merges that move you to the desired state


Things are relocatable, so you could even write “alternate” roots for the cards that embed portions of your app


e.g. a Root that just works with the person list stuff


that Root could have it’s own initial state that makes sense for that particular card


much easier to code


(by relocatable, I mean that idents enable your mutations to not care where in the UI things are at)


The Workflow part 1 video on YouTube demonstrates this I think?


I don’t remember exactly


I’ve seen all the videos, which are great! It’s hard, though, to remember everything at once as fulcro offers so many features 🙂


(I’m thinking about doing a series of videos for liberator now, so you inspired me)


So, in the decard-fulcro expression I can use mutations and only the last expression must return a component?


Here’s a useful trick for cards:

(defsc CardRoot [this props]
  {:query [{:test-users User} ...]
   :initial-state (fn [p] {:test-users [...users...]})}


placing something in the root that isn’t really used by the root can still get you normalization


this is a cheat way to put those users into the db table during startup


you still need to link them into the UI, so perhaps it is just better to use :started-callback with merge-component!


That’s the last map of the defcard-fulcro call then?


yes, the last map is card options:


(defcard-fulcro xxx root/Root {} {:started-callback ...})


{:fulcro {:started-callback (fn [app] ...)}}


just need the :fulcro key


Oh, if CIDER had shown me the docstring I would have known


Let me try that, and thanks for the help.


defexample is just a macro, similar to defcard


but for the book


same concept


With Fulcro version changed to [fulcrologic/fulcro "2.2.1"] in Fulcro Sample Application -- *SSR* reports Cannot route: Unknown Screen in rendered HTML output for main page (normally redirects to login page). Only with Server Side Rendering; client side works and overrides when ClojureScript loaded.


Hm. I didn’t think I did anything to break.


I forgot to test routing changes against SSR 😞


could you report an issue please?


Maybe the sample application is just out of date? Report issue on the sample application?


no, to Fulcro. Routing changes affect SSR on template application.


it should be a transparent change…nothing should break


if something breaks and all you did was a version bump, then I consider that a Fulcro regression


Did a git pull on the sample application and version bump. Not sure if I did a lein clean though; will try first.


Will report issue.


confirmed problem


2.2.2-SNAPSHOT should fix it.


That's fast! Thanks, will try snapshot first thing tomorrow morning.


It’s worrisome. The “fix” should have had no effect. So, I’m still puzzled. And I don’t like unsolved puzzles.


Found it. The API wasn’t clear for getting the ident, and I was actually mis-using it.


Couldn't sleep without having tried fix. 🙂 Confirmed fix in 2.2.2-SNAPSHOT. It now indeed works fine!


@maridonkers still have to figure out why that change caused a problem…it hsould have been ok 🙂


thanks for the report


2.2.2 on clojars


@tony.kay One thing I’m unclear on in the “wire from leaf to root” is what happens at the Route level. In moving some code from devcard over to main/ui and putting the component within router controlled page - I have somehow wired up the components so that I’m getting a Assert failed: get-ident invoked on component with nil props — within a component that has initial-state set and shown in the inspect-data graph. Assert happens in the call (prim/transact! this [(load-grid {:pgrid-id id :grid-id grid})])` Code currently uses the bootstrap routing example. My Root queries the MainRouter -- should the Root also query and potentially get-initial-state for each of the screens referenced by the MainRouter? Currently my naive first-cut was for Root to reference a container component which is within one of the router-controlled screens.


Hey Don. Chances are you’ve pointed an ident somewhere in your state at an entry that doesn’t exist….thus you’re getting empty props on UI refresh, and that leads to this message.


Root should compose in the state of the main router. The router composes in state for all subscreens it controls (not Root…it is a tree). ALl of those screens must have initial state as well


Also, your ident functions must be consistent. This could also happen (as it did this morning for @magra) if your ident function returns the wrong thing.


I’m trying to think of something that can help with this issue more. It’s usually data consistency, but to be honest I got bit by it one or two days ago as well. It’s a nuance that is easy to screw up, and is so small it is hard to see.


So, a more explicit answer to your specific initial “unclear”: The generated component from defrouter: - Compose the queries for all subscreens - Compose the initial state for all subscreens - Uses your supplied ident at the union ident function, which must match (in functionality) all of the screens under control


and the router is just below that. That’s all defrouter does


emits those two components


(you know the bottom one by the name you supply, the top one is sorta hidden)


Thanks @tony.kay. Breaking for dinner (on EST time) - and I’ll take a closer look. I forgot to mention - I am getting the warning PropertyContainer's ident ([:prop-container/by-id nil]) has a nil second element. on PropertyContainer and two other components, so somewhere there is an ident that is off!


more likely the state of the component doesn’t have the data that the ident function is trying to pull