Placeholders

Flattening your data makes it more convenient for the use because it increases the connections of the data, facilitating the access. But sometimes while developing user-interfaces the UI will require some structuring. For example, let’s say you have a user who participated in a group, so you can access :user/id, :user/name, :group/id and :group/name, as in:

{:user/id 1
 :user/name "User"
 :group/id 42
 :group/name "Bar"}

Then we have a component to render the group header.

(fp/defsc GroupHeaderView [_ _]
  {:ident [:group/id]
   :query [:group/id :group/name]})

Now it’s time to create a component for the user, but we want to use the GroupHeaderView to display the user group header. In Fulcro, this means from the user we need to make a join to query for the GroupHeaderView, something like:

(fp/defsc UserImageView [_ _]
  {:ident [:user/id :user/id]
   :query [:user/id :user/name
           {??? (fp/get-query GroupHeaderView)}]})

To fill in the ???, the trick is to make some namespaces special, they make an edge on a graph that keeps the same context as the previous node. In the default setup the namespace > is the special one, so you can use anything with that, examples: :>/group :>/anything…​

This way we can conveniently reshape the data to give it more structure.

Let’s fill the example:

(fp/defsc UserImageView [_ _]
  {:ident [:user/id :user/id]
   :query [:user/id :user/name
           {:>/group (fp/get-query GroupHeaderView)}]})

The final query will be:

[:user/id :user/name
 {:>/group [:group/id :group/name]}]

Which will result in:

{:user/id 1
 :user/name "User"
 :>/group {:group/id 42
           :group/name "Bar"}}

; compare to the original data:

{:user/id 1
 :user/name "User"
 :group/id 42
 :group/name "Bar"}

Take a moment to think about what this means; this feature offers you a dynamic way to break the structure of any arbitrary data into any number of levels when used with the flattening-of-data idea you get the best of both worlds, where one entity can hold as many attributes as they can (as long as there are no ambiguity) and at the same time break that in many smaller components which render specific parts of it.

If you look at the parser’s default configuration, we set the key ::p/placeholder-prefixes #{">"} in the environment. This set will be used by the p/placeholder-env-reader and make a join using the given key while maintaining the context. Plugin and reader implementors can take advantage of this available information (placeholder namespaces) so they can be handled accordingly.