Skip to content

Instantly share code, notes, and snippets.

@holyjak
Last active February 19, 2022 18:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save holyjak/8f5a5ad4924d51ff5500c6c5b6157246 to your computer and use it in GitHub Desktop.
Save holyjak/8f5a5ad4924d51ff5500c6c5b6157246 to your computer and use it in GitHub Desktop.
Fulcro Modelling Puzzles - how would you model a requirement in code?

Fulcro Modelling Puzzles

Self note: see Obsidian

Note
Work in progress

How would you solve a requirement using code?

For some requirements it is easy to see how to map them onto the functionality and structure that Fulcro offers, while for others it might not be trivial. The goal of this document is to collect various examples of such non-trivial cases, eventually with solutions, so that people can better learn to use Fulcro.

Tip
Please contribute by leaving comments!

General

Master-slave siblings

You have two components next to each other, where the left one is a list of bands you select from and the right one shows details about the selected band:

|band1|      | band2 details |
|band2|      | ...           |
|...  |      |               |

From the UI point of view, these are siblings, both children of the Root.

How do you model the data so that this works?

A Row with edit vs. show mode and sub-components, loading on-demand

You have a table with rows of data. It should be possible to switch any row into an "edit" mode. The Edit and View mode are so different that they are handled by different components, and these also have quite different data needs. We do not want to ask for the edit data until a query is switched into the edit mode because we believe it is important for performance.

Notes:

  1. We cannot use a router to switch between the two because it needs to be a singleton while we would need 1 per row

  2. Union queries come to mind but it seems set-query! does not work for an instance, only for the whole class

  3. If the data is loaded from a backend, then we could query for both (and have (if edit? (ui-edit…​) (ui-show …​)) in the body) and the initial load could load :without the edit props, that would be loaded on-demand from the "start editing" mutation via df/load-field or similar.

Loading a global piece of data into a component

From a user:

I have been trying to input a client secret from the server into my Fulcro app for several days now. I tried df/load! and m/returning with a data-only component that has a singleton ident. In the latter case, the data is loaded into the singleton component, but I do not know how to get it into the component that uses it. I can share some of the code if anyone has a suggestion as to what to do. Thanks!

A dynamic, data-based UI

Imagine you want the UI to be fully dynamic - the backend returns a tree of "widgets" and the frontend should render those. Let’s assume all types of widget query for the same props (though they might need different, UI-only data). How to model that?

Answer by Tony

So, fully dynamic UI can be very expensive from a sustainable software architecture viewpoint, but the implementation is something I’ve talked to others about and have helped clients implement. From a Fulcro perspective the structure I recommend is to have a single Component class with a recursive query, and use multimethod dispatch for the rendering.

(declare ui-node)
(defmulti render-node (fn [props] (:node/type props)))
(defmethod render-node :div [{:node/keys [children class]}]
  (dom/div {:classes [class]}
     (map ui-node children))) ; note the recursive call to ui-node
(defmethod render-node :text [props]
   (:node/value props))
(defsc DOMNode [this props]
  {:query [:node/id
           :node/type
           :node/class
           {:node/children ...}]
   :ident :node/id}
  (render-node props))
(defn ui-node (comp/factory DOMNode {:keyfn :node/id})
(defsc Root [this {:root/keys [node]}]
  {:query [{:root/node (comp/get-query DOMNode)}]}
  (ui-node node))
(df/load! app :desired-ui/root DOMNode {:target :root/node})

Where the server returns something like

{:node/type :div
 :node/id some-random-uuid
 :node/children [{:node/type :div
                  :node/id some-random-uuid
                  :node/children [{:node/type text
                                   :node/id some-random-uuid
                                   :node/value "Hello world!"}]}]}

Edit a part

Vi have a travel Leg component with source, dest. cities and a CityPicker component for each. How does the picker access and change the data? Does it have a query and :city/id ident? …​

non-modelling puzzles

In 3.2.4 the order of route-deferred actions was changed to depth-first to prevent flickering. I have the following nested route in my app: Component segment: “/contacts/:id” Child Component A segment: “/files” Child Component B segment: “/comments” Now when I route to “/contacts/1/files” I will get the deferred-route action to load data on the files screen first. But that does not get the :id route-param to know for which contact to load those files. Is there a way to retrieve that route param in dynamic routing in the nested route?

— timovanderkamp
#fulcro 2021-06-08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment