Fork me on GitHub
#om
<
2017-02-16
>
levitanong07:02:36

Hi all, what’s the idiomatic om.next way to represent an entity in many ways? Given an person entity, one might want to display this person in a list or as a card, or show more or show less. Should one just make one component which accepts a display-mode argument? Or should there be several components that declare their own queries and render their own way? If so, which one should declare the ident? Should they all declare idents? etc...

danielstockton07:02:45

@levitanong I think that will depend on your specific use-case, there are no general rules like that. In general, follow react best practices. Om.next just helps you decouple your state management and update components more efficiently when that state changes. Idents refer to specific values in the state, either at the root or within an om.next table (part of the normalization process). If you're component relies on a specific entity, it's good to declare an ident so that om can update it efficiently if that state changes.

levitanong07:02:38

@danielstockton I think my issue is more related to om.next’s queries and normalization, but I’m having a hard time putting it down on words. Will think more on how to phrase it

danielstockton07:02:33

It helps to put it into plain react context imo. In react, everything gets passed down from root. It's the same in om.next, all component queries must compose to root and then om passes everything down for you. React has forceUpdate and shouldUpdate to re-render individual components. Om leverages the queries to help with this, needing nothing special on your part.

danielstockton07:02:35

It's just a way of declaring what data each component needs, rather than explicitly passing everything down, and lets you decouple the state management from writing components.

levitanong07:02:40

@danielstockton would you say that it’s perfectly fine for the UI tree to have two slightly different lists of the same group of things?

levitanong07:02:16

so like, one list of users with a certain set of fields, and then somewhere else, the same list of users with a different set of fields

danielstockton07:02:30

Sure, if that leads to a good UX for whatever you're trying to do. There are no hard rules like that, as long as you understand your own design goals and the trade-offs involved with implementing them.

levitanong07:02:04

:thinking_face:

danielstockton08:02:02

Om queries encourage each component to ask for what it needs, nothing more. It can help if the queries compose in such a way that you don't have to fetch the same data more than once from the backend in terms of performance, you can think about that.

danielstockton08:02:58

Also, it's always the parent component query that includes the queries of the children with (om/get-query Component). If you have a parent component that is shared by these two lists, you might want to think about making their queries the same (including all data needed for both lists), rather than having two similar but slightly different queries that don't compose on the backend.

danielstockton08:02:42

They don't have to be the same actually, you can compose them into one 'merged' query in the parent, using the full power of clojure..

wilkerlucio09:02:13

danielstockton: this may not be a good idea, merging queries would make Om lose track of which component points to what, because you lose the meta (you can't have 2 different metas for the component definition on the same query), and that affects the incremental rendering and may cause you "Can't find path for component..."

wilkerlucio09:02:54

this is why is probably preferred to use multiple paths on those situations, my recommendation is to use "placeholder nodes", nodes that on the server deepen the query level but stay at the same logical place

danielstockton09:02:30

Good point, I've hit that 'can't find path for component' problem before.

wilkerlucio09:02:47

example: [{:ph/thing [:name :age]} {:ph/thing-2 [:name :other]}]

wilkerlucio09:02:30

and on the server you can read for ph namespace and do the magic 🙂

danielstockton09:02:34

That's a good idea, thanks 👍

wilkerlucio09:02:49

no problem 🙂

levitanong08:02:16

Thanks for the answer, @danielstockton

danielstockton09:02:55

@levitanong @wilkerlucio made a good point in the replies above, you shouldn't really merge queries on the frontend.

levitanong09:02:20

yeah, you lose metadata

levitanong09:02:42

oh! threads exist

hlolli10:02:51

So om/force is working for me, mostly. It fetches data and the data is merged into the app-state. But randomly doesnt trigger re-read. As cmcfarlen suggested yesterday, this is my read

(defmethod read :search/search
  [{:keys [state ast target] :as env} k params]
  (if target
    {:remote (assoc ast :params (get @state :search/queries))}
    {:value (get @state k)}))
and this a typical transaction that triggers it
(om/transact! this [(list 'filter-statuses!
                              {:status status-buffer})
                        (om/force [:search/search])
                        :search/search])
I duplicate :search/search to re-enforce the re-read, but I may be tackling this from the wrong direction, is it maybe possible to tell the reconciler to do a re-read when the response arrives/after app-state merge?

hlolli10:02:51

btw om/force seems to only work for keys within vector, causing the response to be {[:search/search] [....]} instead of {:search/search [...]}, this could potentially distrupt the read the reconciler is attempting to initiate?

hlolli10:02:33

yes seems to have been the case, the spirit of the rubber duck came over me.

static om/IQuery
  (query [this]
              [[:search/search '_]
              [[:search/search] '_]
              ...and more... ])
looks like the reconciler wont know that :search/search needs re-read when it gets response with [:search/search] hence, goes looking for forementioned query.

hlolli10:02:52

nonetheless, could be an occacion to open ticket for om/force so that it can accept keywords as well as vectors as argument.

danielstockton11:02:34

@hlolli Maybe that's why it never worked for me. In any case, if true, the documentation needs an update: https://github.com/omcljs/om/wiki/Documentation-(om.next)#force

hlolli11:02:02

ok let me double check, the documentation seems to indicate the opposite.

hlolli11:02:31

yup I was right, only keywords throws me exception

cljs$core$ExceptionInfo
cause: null
columnNumber: undefined
data: cljs.core.PersistentArrayMap

danielstockton11:02:20

Looking at the source, it seems like the documentation is correct and it should be a keyword, because transact! accepts keywords and force simply adds some metadata to those keywords.

hlolli11:02:01

could it be a thing with namespaced keywords.. 😕

danielstockton11:02:32

Doubt it, I definitely tried force with a namespaced keyword and it didn't throw an exception, although I seem to remember it didn't have the desired effect of calling a remote read either. Don't have a specific example to test at the moment.

hlolli11:02:11

ok, let's keep an eye on this function then

drcode15:02:37

@levitanong The OmNext UI query tree is designed to handle lots of duplication in a clean way- The job of the parser functions is to resolve the impedance mismatch between the query tree and the data model of the app-state, and the latter should be free of data duplication. If you have data that you need to display in 3 different formats, create three different ways to query the data and don't be afraid to let data duplication happen.

levitanong15:02:07

awesome, that’s comforting to know @drcode