Fork me on GitHub
#untangled
<
2016-05-25
>
ethangracer15:05:10

@jasonjckn: I’m working on a project that is half high-level overview of the component technologies, half tutorials for building basic UI that will tie back code samples to those high level concepts. If that’s the thing that you feel may help your team, feel free to ping me with ideas and thoughts https://github.com/egracer/untangled-intro

folcon17:05:54

@ethangracer: Thank you for this, I’ll give it a whirl 🙂...

ethangracer17:05:28

great, still very much in development — whole sections not finished yet (currently no coding sections). my goal with the sections finished so far is to simplify the high level overview to a point that it makes sense but doesn’t sacrifice technical accuracy or depth. any feedback is definitely appreciated

jasonjckn20:05:32

@ethangracer: wonderful stuff, yah this will help the team, looking forward to seeing you fill it out

jasonjckn22:05:12

are 'links' suppose to work at any depth in the root query? from the docs it sounds like they always bring you back to the root of the database

jasonjckn22:05:12

so I wrote as a root query

{:foobar [{[:todo-list/by-id '_] [:title]}]}
which works as expected, but if its 2 layers deep it doesn't work
{:foobar [{:foobar [{[:todo-list/by-id '_] [:title]}]}]}

ethangracer22:05:52

so, usually you want to use links to access top-level data, because requesting a table with idents will return a lot of data

tony.kay22:05:11

erm...right form, but you don't access tables that way...use an ident for that

tony.kay22:05:44

[{[:todo-list/by-id 42] [:title]}]

jasonjckn22:05:58

that should work at all depths?

jasonjckn23:05:00

i'll test that..

jasonjckn23:05:24

my db is {:foobar {:foobar {}} {:todo-list/by-id {...}}}

tony.kay23:05:28

Yes, any depth. links should go to a top-level key...e.g. app state might say {:item [:list/by-id 3]} and then a query joined on [:item '_] would work

ethangracer23:05:53

@tony.kay: can you add an ident at any level of the query to get at the data at that ident?

ethangracer23:05:00

I thought that links were a special query form

tony.kay23:05:04

Idents point to items in tables. Links point to top-level keys which in turn are linked via idents

tony.kay23:05:28

links are specifically the term for idents that have '_ for their ID portion

tony.kay23:05:41

which means "top level singleton"

tony.kay23:05:51

if it isn't a singleton, then you need an ident

tony.kay23:05:08

both, by definition, start at the root of the app state

tony.kay23:05:15

since idents are hitting tables

jasonjckn23:05:55

i just tried that I have a root query of

{:foobar [{:foobar [{[:todo-list/by-id 1] [:todo/title]}]}]} 
and a database of
{:foobar {:foobar {}} :todo-list/by-id {...}}
the props passed to Root are :foobar {}

tony.kay23:05:33

that is not a normal query, it is a union

tony.kay23:05:42

do you mean to imply it is in a vector? (e.g. just forgot to copy that bit)

tony.kay23:05:02

Also, I assume your nested table has a key for 1, and a value there that has a :todo/title

jasonjckn23:05:10

i left out the outer [ ]

jasonjckn23:05:46

i'll query at the REPL the exact data/query i'm working with and paste here to clarify

jasonjckn23:05:42

query

[:ui/support-visible :ui/react-key :ui/locale {:foobar [[:items _] [*]]} {:todos [:db/id {:list/items [:db/id :item/label :item/complete :ui/editing]} :list/title :list/filter]}]
initial state
:initial-state {:list            (or (get-url-param "list") "main")
                                     :todo/by-id      {1 {:db/id 1 :todo/title "got milk?"}
                                                       2 {:db/id 2 :todo/title "bar"}}

                                     :todo-list/by-id {1 [[:todo/by-id 1]
                                                          [:todo/by-id 2]]}

                                     :items           [[:todo/by-id 1]
                                                       [:todo/by-id 2]]

                                     ;;:main            {:dev {:todos [:todo-list/by-id 1]}}
                                     :foobar          {:foobar {:foobar {:foobar nil}}}

                                     :todos           {:list/title  ""
                                                       :list/items  []
                                                       :list/filter :none}}

jasonjckn23:05:53

so that one is 1 layer deep and works just fine,

jasonjckn23:05:01

if I change the query to 2 layers deep it won't work

tony.kay23:05:10

that is hand normalized...has to be in an atom to do that.

tony.kay23:05:39

you're probably getting errors in the console on start

jasonjckn23:05:14

no errors just

[196.704s] [om.next] RERENDER: NOTE: If your UI doesn't change, make sure you query for :ui/react-key on your Root and embed that as :key in your top-level DOM element

jasonjckn23:05:21

okay, so don't hand normalize initial state, got it

jasonjckn23:05:25

and that should fix the issue?

tony.kay23:05:57

So, remember that the UI query is used for normalization..do you have queries on your UI? If so, are you composing them together? Do you have idents on your UI?

tony.kay23:05:21

If you want to just play with raw data and raw queries, use db->tree directly

tony.kay23:05:11

If you plug an app database into untangled without an atom, it tries to use the UI query to normalize it. If you use a raw (non-composed query) then metadata will be missing from the query for normalization.

jasonjckn23:05:18

yah, these queries are on the UI

jasonjckn23:05:40

but i did the nesting of :foobar :foobar manually, there's no idents behind them

tony.kay23:05:46

well, and the query looks incorrect, too:

tony.kay23:05:49

{:foobar [[:items _] [*]]}

jasonjckn23:05:00

that works fine 1 layer deep though

jasonjckn23:05:11

i don't think the issue is around [[:items _] [*]]

tony.kay23:05:14

it isn't a join

tony.kay23:05:44

the inner part, that is

tony.kay23:05:33

I mean, you can probably use a link without a join...but you'll get a raw dump of the thing at the location in state

tony.kay23:05:41

you won't be selecting keys from it

jasonjckn23:05:52

ok, sorry, that was a typo, my other tests didn't have that typo

jasonjckn23:05:00

trying queries again

jasonjckn23:05:37

[:ui/support-visible :ui/react-key :ui/locale {:foobar [{:foobar [{[:items _] [*]}]}]} {:todos [:db/id {:list/items [:db/id :item/label :item/complete :ui/editing]} :list/title :list/filter]}]

jasonjckn23:05:42

the join is in there

jasonjckn23:05:55

i can try baking :foobar into the UI next

jasonjckn23:05:17

;(defui ^:once NewComp
  static om/Ident
  (ident [this props]
    [:newcomp 1])

  static om/IQuery
  (query [this]
    [{:foobar [{[:items '_] '[*]}]}])

  Object
  (render [this]
    (dom/div nil
             (prn-str (om/props this)))))
(def new-comp (om/factory NewComp))

(defui ^:once Root
  static om/IQuery
  (query [this] `[:ui/support-visible
                  :ui/react-key
                  :ui/locale
                  #_ {:main [{:dev [{:todos ~'[*]}]}]}
                  {:foobar ~(om/get-query NewComp)}

                  #_ {:ui/main [{:dev [{:todos [:db/id :todo/title]}]}]}
                  {:todos ~(om/get-query TodoList)}])

jasonjckn23:05:21

this code works great though

(defui ^:once NewComp
  static om/Ident
  (ident [this props]
    [:newcomp 1])

  static om/IQuery
  (query [this]
    [{[:items '_] '[*]}])

  Object
  (render [this]
    (dom/div nil
             (prn-str (om/props this)))))
(def new-comp (om/factory NewComp))

(defui ^:once Root
  static om/IQuery
  (query [this] `[:ui/support-visible
                  :ui/react-key
                  :ui/locale
                  #_ {:main [{:dev [{:todos ~'[*]}]}]}
                  {:foobar ~(om/get-query NewComp)}

                  #_ {:ui/main [{:dev [{:todos [:db/id :todo/title]}]}]}
                  {:todos ~(om/get-query TodoList)}])

tony.kay23:05:50

so first, try putting your state in an atom, so it knows it is already normalized

jasonjckn23:05:00

i tried that it just broke the whole app

jasonjckn23:05:14

i'll need to dig deeper with that one

jasonjckn23:05:24

few minutes... there

jasonjckn23:05:13

okay, so doing that makes neither of the 2 codes above work

jasonjckn23:05:21

if i pass an atom to initial-state

tony.kay23:05:17

what is the error?

jasonjckn23:05:06

fixed it, parinfer messed up parens

jasonjckn23:05:08

i turned it off

jasonjckn23:05:15

back on track

tony.kay23:05:18

and if you give a component an Ident, then it has to exist in the app state at that db table location

tony.kay23:05:28

or re-render and all manner of things go sideways

jasonjckn23:05:29

okay, i'll fill in that ident

tony.kay23:05:52

Basically, your ident functions need to follow the structure of your db

tony.kay23:05:12

If you compose in a component that gives an ident, then the data for it better exist in a table at that ident location

jasonjckn23:05:30

oh cool, they both work now! both 1 and 2 layers of foobars

jasonjckn23:05:21

yah it was the atom on initial state that did the trick

tony.kay23:05:24

Using an atom turns off auto-normalization

tony.kay23:05:36

That needs better docs, perhaps.

tony.kay23:05:49

just turns it off for initial state though

noonian23:05:31

I suspect there is a bug right now as well. Afaict you can’t force auto-normalization at all when passing in an atom with the latest alphas; you used to be able to pass in :normalize true when constructing the reconciler but that seems to have stopped working.

noonian23:05:38

Noticed that last night.

tony.kay23:05:55

Hey Jed, Hm. I'll have to look at that

tony.kay23:05:59

That would be a breaking change

noonian23:05:17

Howdy 🙂

noonian23:05:28

norm? will be true when passing in an atom and in add root it only normalizes if the :normalize flag is set and norm? is falsey.

tony.kay23:05:05

Yea, I did that. Makes it possible to do all of the variants you might want

noonian23:05:54

I don’t understand that. How can you pass in an atom and still get it to normalize since norm? will always be true and thus it will never normalize during add-root?

tony.kay23:05:44

So, if you pass in an atom, Om knows you hand normalized it.

noonian23:05:48

I worked around this by just passing in a map and getting a reference to the atom after the reconciler was created using om/app-state.

tony.kay23:05:52

If you pass in a map, it knows you did not

tony.kay23:05:07

you can't...but you don't have to

noonian23:05:29

right, but you used to be able to pass in :normalize true in the reconciler to tell it that your atom is not normalized (and enable normalization for merged etc.) but that no longer works after this change.

tony.kay23:05:31

you either want normalization ON or OFF in the entire app. Untangled ALWAYS turns it on

tony.kay23:05:48

Right, you used to, but that made it impossible to do the thing you really needed

noonian23:05:02

hehe, which is?

tony.kay23:05:08

Pass in hand-normalized data, but also ENABLE normalization on merge

noonian23:05:13

I’m just talking in the context of Om next and not untangled btw.

tony.kay23:05:23

that is the more common case...it is trivial to NOT pass an atom to say you didn't normalize it

tony.kay23:05:59

so, now it is possible to do every case, just the API changed slightly...don't pass an atom if you mean to indicate non-norm data

noonian23:05:16

It seems like the :normalize key has zero effect now.

noonian23:05:41

every case except pass in a pre-existing atom and turn on normalization

tony.kay23:05:55

:normalize does exactly one thing, ever: turns on or off auto-normalization (for merging novelty on server returns, and initial state iff it is not an atom)

tony.kay23:05:21

This is an Om concern, since normalization is always on in Untangled

tony.kay23:05:38

The choice of an atom simply lets you choose to hand-norm your initial data

noonian23:05:41

I agree, my question is really is it intended that the public api should change in this way.

tony.kay23:05:23

Discussed it with David and Antonio...there was no way to get the case some people needed with the old behavior

noonian23:05:48

which was pass in a map and keep normalization off?

tony.kay23:05:34

Cases: 1. You want normalization everywhere: pass a map as initial state, and normalize true 2. You want normalization everywhere, except initialization: pass an atom and normalize true 3 and 4. You don't want normalization: pass in whatever the hell you want for initial state and set normalize false

noonian23:05:26

Ah, so it was case 2 that was unsupported

tony.kay23:05:26

so now all 4 possible combos are covered

tony.kay23:05:04

and it is a case I wanted for Untangled

noonian23:05:07

It looks to me that you could handle all cases and allow passing in an atom with normalization turned on by adding another param to reconciler like skip-initial-normalize? and instead of :normalized norm? when creating the reconciler state you’d have :normalized skip-initial-normalize?. That way it wouldn’t be a breaking change either.