Fork me on GitHub
#om
<
2016-06-12
>
jasonjckn00:06:54

it works with singletons, but not vectors, i find that odd

jasonjckn00:06:24

e.g.

(om/db->tree `[{:bar [:foo]}] {:bar {:foo 3}]} {})
works

iwankaramazow11:06:21

@jasonjckn: I think you're looking for:

(om/db->tree [:foo] [[:bar/bars-by-id 1] [:bar/bars-by-id 2]] {:bar/bars-by-id {1 {:foo "foo1"} 2 {:foo "foo2"}}})
That gives you:
[{:foo "foo1"} {:foo "foo2"}]

iwankaramazow11:06:41

Or with the full query you could do something like:

(om/db->tree [{:bar [:foo]}] {:bar [[:bar/bars-by-id 1] [:bar/bars-by-id 2]]} {:bar/bars-by-id {1 {:foo "foo1"} 2 {:foo "foo2"}}})

iwankaramazow11:06:49

That gives you:

{:bar [{:foo "foo1"} {:foo "foo2"}]}

iwankaramazow11:06:28

Take a look a the source or docs:

om.next/db->tree
([query data refs] [query data refs map-ident])
  Given a query, some data in the default database format, and the entire
   application state in the default database format, return the tree where all
   ident links have been replaced with their original node values.

jimmy12:06:17

I have a list of items normalized in database. Then in details page, I want extra attrs from the selected item, what is a good approach to this ?

iwankaramazow12:06:24

@nxqd: something like [{:list [:foo :bar]} {:detail [:foo :bar :baz :else]}]?

jimmy12:06:59

@iwankaramazow: what I would like to know is, when I load the list pages, I have a list of normalized items only have [:foo :bar]. But then in details page, I want more, so I would load more attrs for a particular item in those normalized items already in state. So in this case, should we do another query to backend to get more attrs, then merge it manually, or do some sort of mutation ?

iwankaramazow12:06:51

This is a read, nothing gets mutated here. You'll have to diff the keys that are available on the client. The ones that aren't make it to the remote

jimmy12:06:27

true I have tried the same approach. I think I miss something here. This is what I tried: create a read like this :project/get I check if the one I get in state has the same keys I request or not, if not I send it to remote.

jimmy12:06:12

I think I should do 1 more step to merge the result returned to current state ? or will it be automatically merged ?

iwankaramazow12:06:12

The remote read should be merged in automatically. If the remote results arrives, does your app state contain the extra keys?

iwankaramazow12:06:18

Or doesn't it re-render?

jimmy12:06:35

it doesn't re-render in my case

iwankaramazow12:06:36

can you show me some code from your parser?

jimmy12:06:51

(defmethod read :project/get
  [{:keys [parser target query state ast] :as env} k {:keys [id] :as params}]
  ;; if we have id then we want details project/by-id which have more attrs
  (let [st @state]
    (if id
      (let [val (get-in st [:project/by-id (uuid id)])
            ks (into [] (keys val))]
        (if (or (nil? val) (not= ks query))
          {:remote true}
          {:value val})))))

jimmy12:06:00

this is the read function I use to get more attrs

iwankaramazow12:06:51

I think you have to return a remote and a value together.

iwankaramazow12:06:17

{:remote true :value val}

jimmy12:06:36

ok, let me try

jimmy12:06:56

I can see there is value returned but it's an old value, the new one is in @state

jimmy12:06:19

but not normalized into project/by-id table. but it's one step closer

iwankaramazow12:06:45

hmm, I'll cook up an example. Give me an hour 😄

jimmy12:06:55

oh thanks a lot

jimmy13:06:47

hmm, I can see that there is value being merged here, but in the code above, id now is nil so it doesn't go throw and return the new value. The problem might be, the read function is being reread but the params is not persisted.

iwankaramazow13:06:04

What if you change the read to [{[:project/get some-id] [:foo :bar :bas]}]

iwankaramazow13:06:42

ids should probably go straight into the query as an ident

jimmy13:06:28

this is how I do the read :

`[({:project/get ~form-query} {:id ~'?id})]`

jimmy13:06:36

it's true, didn't know that, let me try

jimmy13:06:39

I change to

`[{[:project/get ~'?id] ~form-query}]`
but there is error regarding the query ( long error stack ) http://i.imgur.com/zDKFwZz.png

iwankaramazow13:06:06

[{[:project/get id] ~form-query}] ?

jimmy13:06:38

yeah, I do use id from queryparams ( pasted the wrong old one in the last comment )

iwankaramazow13:06:18

I'll try writing an example

jimmy13:06:30

hmm, I just found out that the problem occurred when I change the params with (om/set-query!)

iwankaramazow13:06:30

the re-read doesn't happen?

jimmy13:06:10

when I use set-query! to chane the id to the the correct one, the error I posted above happens.

iwankaramazow13:06:08

om/db->tree can handle idents I thought

jimmy13:06:22

yeah, hmm but only get the ~form-query in query variable in read function. How can I get the whole thing :-? including the id. Ah ok I got it, let's see, it's a bit complicated T_T

jimmy13:06:42

but btw :project/get is not :project/by-id

iwankaramazow13:06:10

yea it's a bit more complicated than I thought 😄

iwankaramazow13:06:16

I'm trying it out

jimmy13:06:24

we only have :project/by-id normalized in our db. :project/get is just a function we use to get more attrs,

jimmy13:06:01

I see some people try to persist some value into the state like :project/get-id 123, then they get it back when the result returned. It would be a solution but it's a bit hacky.

iwankaramazow13:06:36

I thought of that, it should be possible with params only

iwankaramazow13:06:48

Might take me some time to figure it out 100%

peeja14:06:38

I just worked out exactly this

peeja14:06:39

I'm not entirely following what's failing for you, but that may provide a reference to see what you're missing

iwankaramazow14:06:03

@peeja: thanks, but I just finished a similar example 😄

jimmy14:06:07

@peeja: interesting case of using multiple but if we have multiple details page, we will end up with multiple remotes as well ?

jimmy14:06:46

@iwankaramazow: have you figured something out ? I'm really out of idea xD

iwankaramazow14:06:13

@nxqd: Yea I've got it working 😄 just posting it to github gist, a second

jimmy14:06:21

woa ! nice 😄

peeja14:06:03

@iwankaramazow: Well, you'll end up with a remote for every API endpoint. In the case I'm abstracting, there was already a single list endpoint that had a few attributes on each item, and another endpoint with more attributes for a single item.

peeja14:06:24

Er, sorry: that was for @nxqd 🙂

peeja14:06:41

@iwankaramazow: Glad it's working! Looking forward to seeing what you did.

iwankaramazow14:06:02

will take me 10 more minutes, I forget the query diffing

jimmy14:06:24

@peeja: aha, actually we can replace multiple remotes with a read, it would work the same.

iwankaramazow14:06:05

if you run it, you'll see :title in the list and :title :description in the selected detail

jimmy14:06:03

nice, good fake db though, it makes much easier to make short example. I will check it, it seems to be the same approach I took but the id seems to be persist

iwankaramazow14:06:12

fixed a syntax problem in the gist

jimmy15:06:10

@iwankaramazow: yeah it works flawlessly, I think there is something wrong with my code that the id is changed to nil when it being reread.

iwankaramazow15:06:39

@nxqd: just finished the example with query-diffing 😄

iwankaramazow15:06:13

It'll only fetch :description if :title is available in the app-state.

jimmy15:06:37

yeah I can see that, pretty nice, especially for mobile 😄

iwankaramazow15:06:26

Om Next is just awesome 😄

jimmy15:06:21

hmm, I still wonder how come the id in mine returned nil after the new value being merged to om next db

iwankaramazow15:06:20

@nxqd: the id in your app-state is nil?

jimmy15:06:44

ah, no the id param in the read function :project/detail

iwankaramazow15:06:09

does it get updated in the meantime? something that resets it ?

jimmy15:06:11

I output the @state, when the new value merged in, the id is nil, weird

iwankaramazow15:06:25

don't forget to return the id from your backend

anmonteiro18:06:30

TL;DR: this is the solution I talk about in my post, a library I made available just now https://github.com/anmonteiro/compassus

iwankaramazow18:06:20

@anmonteiro: Great work! Does this support nested routing?

iwankaramazow19:06:06

You have an App component that needs to be displayed on every page. App contains a query. Inside App you want to render the specific pages, let's say About page on /about and Profile page /profile. The both contain another query. How should I implement this with compassus?

(def routes-in-pseudocode
  {"/" {:component App
        :children {"about" {:component About} "profile" {:component Profile}}}

anmonteiro19:06:27

so the common component that’s displayed in every page you specify via a :wrapper key to compassus.core/applicatioN

anmonteiro19:06:56

your route declaration would then be

{:about About
 :profile Profile}

anmonteiro19:06:14

URL/ path navigation is a completely different concern

anmonteiro19:06:37

for which there’s support, of course, but a different concern nonetheless

iwankaramazow19:06:32

Aside from the path/url nav, should it be possible to use a :wrapper on arbitrary levels?

iwankaramazow19:06:10

if profile for example is another container for two children

iwankaramazow19:06:17

or am I stretching things here?

anmonteiro19:06:08

the possibility to supply a wrapper on the top level is for the sole reason that Compassus will create the root component for you

anmonteiro19:06:46

@iwankaramazow: if you want a “wrapper” at the profile level, there’s nothing stopping you from doing that, but you don’t need to hook into Compassus to do that!

iwankaramazow19:06:04

Ok, I think I got it 🙂 Thanks!

anmonteiro19:06:58

@iwankaramazow: something else that I want to be clear about: “nested” routing as in /item/42/owner/43/edit is something that is not included by design

anmonteiro19:06:33

it is my understanding that routing is a top-level concern (read the Disclaimer in my post)

anmonteiro19:06:48

everything else you should be doing in the app-state

anmonteiro19:06:07

e.g. specify the same handler for arbitrarily nested routes, deal with the params in the app-state

anmonteiro19:06:09

hope this is clear

anmonteiro19:06:28

happy to be more specific about this in the README if you feel the information is missing

iwankaramazow19:06:43

It's perfectly clear 🙂

iwankaramazow19:06:56

The design is very sound

anmonteiro19:06:51

Yeah, it’s been brewing for a while

anmonteiro19:06:52

It’s also a goal to keep the API surface very small

anmonteiro19:06:25

Om should have every function you need except from the ones that I provide

iwankaramazow19:06:37

Really great job on this

thosmos22:06:29

@anmonteiro: Compassus looks good! I'm currently using one Bidi route + multiple tags to differentiate some different page contents using the same Page component, like so:

(def routes
    ["/" [["landing" (tag :route/page :page/landing)]
          ["privacy" (tag :route/page :page/privacy)]]])
Could this work with Compassus some how? Or would you suggest instead using unique route names and directing them to the same component like (compassus/application {:routes {:landing Page :privacy Page}})? This will multiply my parse functions.

anmonteiro22:06:43

@thosmos: the problem that Compassus tries to solve is routing to components that have different queries

anmonteiro22:06:20

if your matches always route to the same component, how are you managing queries?

thosmos23:06:03

Most of my routes are different queries, but some of them aren't as in this case of generic pages. Currently my pushy handler converts the tag into a query param on the routed component's static query and then adds that to the static root component's query (I think you call this the "wrapper") and then does a (set-query! reconciler {:query root-query}) The handler does this for every route change.

anmonteiro23:06:45

@thosmos: I think Compassus can definitely support bidi’s tagged matches, but definitely not a set-query! approach

thosmos23:06:07

Yeah seems like Compassus's wrapper might be a better way than doing a set-query

anmonteiro23:06:39

@thosmos: the problem would be that the two approaches are conflicting

anmonteiro23:06:03

so it would seem that if you’re going to move to compassus, you’d have to re-engineer some part of your system

anmonteiro23:06:22

what I’ve found in my projects is that my Root component just becomes the :wrapper

thosmos23:06:54

would the wrapper component be able to change based on the route? i.e. if there's a nav bar in the wrapper it can change to show the current page?

anmonteiro23:06:26

re: tags multiplying parsing functions, you can always use another form of dispatching other than om.next/dispatch

thosmos23:06:32

oh i see it would be using route (c/current-route this)

anmonteiro23:06:42

that would be the answer, yes

anmonteiro23:06:14

current-route is also pretty flexible in the sense that it allows you to pass a component, reconciler or CompassusApplication instance

anmonteiro23:06:38

set-route! too

anmonteiro23:06:54

you can call it from whatever component, even from components that don’t implement IQuery

anmonteiro23:06:45

(even though it call transact! under the hood)

anmonteiro23:06:58

@thosmos: do let me know if anything is not clear. I’m happy to re-write the docs to make them clearer

anmonteiro23:06:36

as well as rethink some of the API stuff that doesn’t reveal flexible enough

thosmos23:06:16

thanks, @anmonteiro looks good. I'll probably try it out on my next om.next app

anmonteiro23:06:35

sounds good! looking forward to hearing feedback