Resolving foreign keys in Om Next

58 views Asked by At

I'm trying to understand the normalization, identity and querying concepts in Om Next. Every time I think I have it, I run into a new situation that seems to baffle me.

I have a remote that returns the following data:

{:users [
  {:id 1,
   :email "[email protected]",
   :role 1},
  {:id 3,
   :email "[email protected]",
   :role 1},
  {:id 4,
   :email "[email protected]",
   :role 1},
  {:id 6,
   :email "[email protected]",
   :role 1}}]
  :roles [
    {:id 1, :name "admin"}]}

I've structured my components like this:

(defui Root
  static om/IQuery
  (query [this]
     `[{:users ~(om/get-query User)}
       {:roles ~(om/get-query Role)}]))

(defui Role
  static om/Ident
  (ident [this {:keys [id]}]
         [:role/by-id id])
  static om/IQuery
  (query [this]
         [:id :name]))

(defui User
  static om/Ident
  (ident [this {:keys [id]}]
         [:user/by-id id])
  static om/IQuery
  (query [this]
         [:id :email :role]))

This results in the reconciler normalizing my data like this:

{:users
 [[:user/by-id 1] [:user/by-id 3] [:user/by-id 4] [:user/by-id 6]],
 :roles [[:role/by-id 1]],
 :user/by-id
 {1
  {:id 1,
   :email "[email protected]",
   :role 1},
  3
  {:id 3,
   :email "[email protected]",
   :role 1},
  4
  {:id 4,
   :email "[email protected]",
   :role 1},
  6
  {:id 6,
   :email "[email protected]",
   :role 1}},
 :role/by-id {1 {:id 1, :name "admin"}}}

Now, as to my question. I'd like to access a user's role's name inside the component. I could simply do something like (get-in @app-state [:role/by-id role :name]) within the render function of the User component, but that doesn't seem like the idiomatic thing to do.

The Om Next document on "Thinking with links" seems to mention my solution: idents. I've tried to use this within the User query (like [:id :email [:role/by-id 1]]) and that works! However, I can't seem to insert the actual role id into the ident! I've tried very complicated things with query parameters, inserting it into the indent like that, but it all seems horribly contrived.

This seems like an incredibly common situation for which a decent solution should exist, but I can't seem to get it!

*EDIT Added the Root component

1

There are 1 answers

8
Chris Murphy On

Your query syntax for User ought to be something like this:

[:id :email {:user-role (om/get-query Role)}]

Then the table state for a user might be:

{:id 6,
 :email "[email protected]",
 :user-role [:role/by-id 1]}

It would help if you showed your defuis in the question, especially for Root. One thing with Om.Next is that all the joins have to be done on the Root component in order to get the app state properly normalized.