1

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 "foo@example.com",
   :role 1},
  {:id 3,
   :email "bar@floyhamilton.nl",
   :role 1},
  {:id 4,
   :email "baz@example.com",
   :role 1},
  {:id 6,
   :email "nom@example.com",
   :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 "foo@example.com",
   :role 1},
  3
  {:id 3,
   :email "bar@floyhamilton.nl",
   :role 1},
  4
  {:id 4,
   :email "baz@example.com",
   :role 1},
  6
  {:id 6,
   :email "nom@example.com",
   :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

Linus
  • 1,113
  • 1
  • 8
  • 14

1 Answers1

1

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 "nom@example.com",
 :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.

Chris Murphy
  • 6,411
  • 1
  • 24
  • 42
  • `:user-role` is a 'join' or an 'edge'. You do just make these joins up as you go along. Are you looking at the state in the REPL? – Chris Murphy Aug 30 '17 at 09:17
  • Does you answer assume that the back-end deals with the User query? Because it's a fairly dumb REST API that will just return the list of users as is. I'm following along both in the REPL as well as in my browser – Linus Aug 30 '17 at 09:21
  • I was assuming the whole thing was in the client. So client state and client components, no calls to the server whatsoever. – Chris Murphy Aug 30 '17 at 09:23
  • Alright, so I've adapted my back-end to return the data in the format you've specified. `:users` is now a vector containining `{:id 1, :email "foo@example.nl", :role 1, :user-role [:role/by-id 1]}`, and the User's query is now `[:id :email {:user-role (om/get-query Role)}]`. However, this results in the following error: `Uncaught Error: No protocol method ICollection.-conj defined for type number: 1` – Linus Aug 30 '17 at 10:14
  • I've added my root component to my question :) – Linus Aug 30 '17 at 10:18
  • What is `:role 1` doing there? I would just be doing all this 'client only'. I would never use stock Om.Next for client/server when Fulcro has a well thought out solution that removes most of the client/server difficulties. – Chris Murphy Aug 30 '17 at 10:20
  • I've removed the `:role 1` pair, but to no avail. Fulcro seems like something I should look in to, but this front-end is not the only user of this API, so I'm stuck with it for now. – Linus Aug 30 '17 at 10:43
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/153231/discussion-between-chris-murphy-and-linus). – Chris Murphy Aug 30 '17 at 10:49