Fork me on GitHub
#untangled
<
2017-02-23
>
adambrosio00:02:01

so for example, we have :workers, that just shows all the workers, but you could have an :exceptional-workers view that showed only the top x% of workers

adambrosio00:02:27

you would query at Root for {:exceptional-workers (om/get-query WorkerRow)} and then pass that to ui-exceptional-workers

adambrosio00:02:47

you then have to filter that list in ExceptionalWorkers/render, but you can do whatever you need to in that method

adambrosio00:02:52

im slightly doubting myself, but i think the alternative is not really provided to you in untangled. ie in just om next you would write a parameterized read method that would filter the workers list as it was de-normalizing but with untangled we’ve made the decision that that is not worth the cost in having to understand and maintain parsing code (which can get quite complicated) instead you just massage the data as you need to in your components

adambrosio00:02:23

id have to think a bit more, whereas @tony.kay could just tell you, but there are more techiniques for having and updating those views

adambrosio00:02:13

i think what untangled pushes you towards is that when you load your workers from the server, you would have a post-mutation that would create another view of that data based on your filter/logic

adambrosio00:02:49

but all you are doing is creating pointers (idents) to the same table of data

adambrosio00:02:14

anyway, wall of text alert!

urbank00:02:47

I like walls of text. Lots of information 🙂

adambrosio00:02:48

before you go thinking im right, i would read the devguide until @tony.kay confirms or adjusts my statements

urbank00:02:06

Right, I will. What I understand of the framework, I like what I see. There's going to be a pretty big rewrite over here, and I've been agonizing for a while on what to use

urbank00:02:11

this seems very promising

adambrosio00:02:52

what’s the rewrite from?

urbank00:02:29

A horrible mess of global listeners via jquery. And a bunch of plugins which are really expensive to load. It's quite painful, but they made it in a hurry (for some reason)

urbank00:02:51

For the main part of the app, there's one big HTML file and one big JS file.

adambrosio00:02:42

are you debating between languages, or cljs libraries?

urbank00:02:55

The HTML is basically this giant empty form. The javascript then inits a punch of plugins. Adding dynamic elements is done by cloning the initial ones on the page (emptying them if needed)

urbank00:02:55

Anyway, we're mostly settled on clojurescript at this point. Not completely discounting other languages though.

adambrosio00:02:57

sounds nasty, like anything would be better but i would say that you will find subtler and more pervasive problems in any other libraries

adambrosio00:02:45

personally i would go with untangled or re-frame

adambrosio00:02:57

re-frame mainly because its a well established pattern and used by a lot of cljs devs (as far as i know)

adambrosio00:02:49

but if you are ok with some minor turbulence and a work in progress, untangled is much more geared towards helping you create a solid enterprise level application

adambrosio00:02:27

i dont think i’ve seen a better fleshed out full stack framework in clj(s)

adambrosio00:02:46

is your app just client side right now?

urbank00:02:14

Well, after fighting with the current mess, I'm not too worried about some turbulence here and there. The main thing is that the core concept is solid

adambrosio00:02:45

you’ll get plenty of that (solid foundations) with @tony.kay‘s stewardship

qqq00:02:22

make the back end support GAE pls 🙂

adambrosio00:02:30

@qqq what does that mean?

adambrosio00:02:46

i’ve written a more modular way to build an untangled-server, but what exactly does supporting GAE mean?

qqq00:02:28

I'e like the backend to be based on datastore + easy to deploy to GAE, rather than based on datomic

qqq00:02:40

client side -- probably nothing needs to be changed

adambrosio00:02:47

there’s no hard dependency on datomic on the backend

qqq00:02:10

yeah, but there's no trivial one-line support for GAE either (I'm working on this myself right now)

qqq00:02:22

right now, I'm trying ot get the untangled-todomvc to run on GAE by changing the server side

adambrosio00:02:17

it seems you are more talking about supporting whatever persistence layer GAE uses with the way we’ve structured the server it should just be a component that you put in the parser-injections and use in your reads and mutations

adambrosio00:02:51

do you need something else that isn’t currently supported by untangled? I dont think that we have the man power to directly write that layer for you, but I can try to help get what you might need from untangled-server into our schedule

qqq00:02:18

@adambros: maybe it's best if I just post questions as I run into problems porting it to GAE

qqq00:02:25

I don't think I currently know enough to even know what to ask for

adambrosio00:02:03

well, do you have a link to something that might give an overview on how to use gae?

qqq00:02:23

yeah, I have a clojure gae setup working fine

qqq00:02:35

I also have the untangled-todomvc client+server working fine; I just need to mash the two code bases togehter

qqq00:02:25

so I started writing my app in gae, using clj on server side + reframe/cljs on client side: -- then I realized "hmm, this om/next thing makes sense since communicating between server/client is a bitch" -- then I stumbled across untangeld and it seems like you guys solved precisely the communication problem I was dealing with

adambrosio00:02:49

yeah untangled does a lot on top of om.next to help you make an application

adambrosio00:02:12

well let me know if you have a specific, or even abstract, question about untangled regarding the server interaction

urbank00:02:49

Hm... so I'm not quite sure what you mean by "is my app just client side right now". If I'm only testing out the clientside of untangled? If so, yes. Unfortunately I have much more say over the clientside than I do over the server. So I'm not very confident if I'll be able to push that one through 🙂

adambrosio00:02:56

I more meant is there a server side you are trying to port over to something else

urbank00:02:35

Oh that. Well, the server code is not nearly as much of a mess, so I think it's pretty likely going to stay as is. At least there's not going to be a huge overhaul.

adambrosio00:02:15

gotcha, well you should be able to make that work by tweaking the untangled-client :networking, i’m assuming its a REST server? ie: /workers /workers/:id /workers/:id/edit

urbank00:02:39

Yeah, It's a REST server. Transitioning to a bit of a cleaner API, so it should be good

adambrosio00:02:41

well so om.next (& therefore untangled) just talk to an /api route by sending it data/queries

adambrosio00:02:29

so reads are [:key {:join [:a :b]}], and mutations are similar [('launch-rockets! {:id 23})]

adambrosio00:02:36

so if the cleaner api is still rest, you will have to write (or find) some translator layer but iirc some people here might have more to say on that

qqq00:02:56

this is very very very minor; but I think untagngled docs can be improved if, for eacy devguide devcard, thre was a link to a github repo *.clj sfile

urbank00:02:16

Yes, I thought it would be something like that. I would look into not using REST, and just going with this full-stack, but I'm not really in charge of that. I do need something flexible and well thought-out on the client. And there aren't that many requests. Most of the complexity, as far as the client-side is concerned, is in this input form.

adambrosio00:02:02

@qqq sounds nice, feel free to file an issue on it

urbank00:02:51

Well, it's getting really late over here. Looking forward to exploring this further tomorrow.

adambrosio00:02:28

nearly 2am kinda late, europe?

adambrosio00:02:26

look out for a message from tony, he’ll likely write something about my earlier comments

adambrosio00:02:18

oh and g'nite

urbank00:02:13

Great, will do! Good night, and thanks again for the help/discussion! (telling someone about the mess that is the current code I'm maintaining was cathartic as well 🙂 )

adambrosio01:02:02

@tony.kay I can confirm that I can fix my problem by having the post-mutations transacted, instead of used just for their :action

adambrosio01:02:32

whether that’s a good idea 😕

qqq02:02:00

1) I already have a basic untangled/devcard/defcard setup working. 2) I have inserted the code

(defui ^:once Counter
  static uc/InitialAppState
  (uc/initial-state [this {:keys [id start]
                           :or   {id 1 start 1}
                           :as   params}] {:counter/id id :counter/n start})
  static om/IQuery
  (query [this] [:counter/id :counter/n])
  static om/Ident
  (ident [this props] [:counter/by-id (:counter/id props)])
  Object
  (render [this]
    (let [{:keys [counter/id counter/n]} (om/props this)
          onClick (om/get-computed this :onClick)]
      (dom/div #js {:className "counter"}
        (dom/span #js {:className "counter-label"}
          (str "Current count for counter " id ":  "))
        (dom/span #js {:className "counter-value"} n)
        (dom/button #js {:onClick #(onClick id)} "Increment")))))
3) Now my question is -- how do I create a minimal devcard which shows the above? (In the devguide, we ahve to create something like 5 more components before we can display any of them). I just want to interact with this one component NOW.

adambrosio02:02:56

what are the 5 more components you are talking about?

qqq02:02:40

does untangled provide CRDTs (or things similar) or is this something users have to make their own decisions on

adambrosio02:02:18

having not heard of those before in any untangled conversation, I would say it’s up to you

adambrosio02:02:11

not sure if it’s related, but one of our teams are using hazelcast...

qqq02:02:44

At minute 11:30 of https://www.youtube.com/watch?v=IeIyQDg9gBc it talks about "merging data"

qqq02:02:10

the only method I know of is: https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type -- (which Phoenix Presence also uses)

adambrosio02:02:50

not sure i understand, what are you wanting to replicate, and across what?

adambrosio02:02:44

this sounds like something your db/persistance layer uses/implements

qqq02:02:45

can we discuss this over DM ? I understand you don't want to clustter the main channe, but this threading UI is really weird to use on my laptop

qqq02:02:25

http://untangled-web.github.io/untangled/guide.html#!/untangled_devguide.A_Quick_Tour -- CounterPanel, Root, SampleApp, "5" may have been an overestimate

adambrosio02:02:15

i’d rather keep it in the channel

qqq03:02:35

sure, let's kill threading though; the window is too narrow on my mbp

adambrosio03:02:25

you can view it full screen by clicking on All Threads above your starred channels

adambrosio02:02:29

so the example you gave you should be able to just plop in a card

adambrosio02:02:43

you dont really need a panel or sampleapp

qqq02:02:02

okay, so I've done that

qqq02:02:06

I now ahve the following problem

qqq02:02:15

are "id" and "n" supposed to be numbers or Objects or what?

qqq02:02:29

when I do

(. js/console log (str "id: " id))
the js console prints it out as:

qqq02:02:39

id: [object Object]

qqq02:02:47

so it seems like somehow I'm not getting the value

adambrosio02:02:21

weird, not sure about that, maybe try prn?

qqq02:02:53

that did not fix it: however, commenting out the: static uc/InitialAppState static om/IQuery static/omIdent lines did fix it I must ahve some type of error in one of those lines

adambrosio02:02:26

static uc/InitialAppState (uc/initial-state

adambrosio02:02:38

but static uc/InitialAppState (initial-state ...

adambrosio02:02:53

you dont namespace the method names

adambrosio02:02:46

maybe? does that fix it or was i wrong?

qqq02:02:06

err, currently trying to fix shy the Increment button throws an error

qqq02:02:56

okay, changing uc/initial-state to initial-state fixed it

qqq02:02:33

these two lines:

(let [onClick (om/get-computed this :onClick)] ...)
^^ how that does line cause onClick to increment?

noonian02:02:05

That is just getting a reference to a prop and calling it onClick. This is where it is using it as an onClick handler: (dom/button #js {:onClick #(onClick id)} "Increment”)

qqq02:02:37

right, so where is the #(+ 1 %) or inc ?

qqq02:02:43

where does the actual "inrementing" occur?

noonian02:02:10

It is assuming that the parent component is passing in a function for that prop that will actually do the incrementing.

noonian02:02:45

In CounterPanel: (map #(ui-counter (om/computed % {:onClick click-callback})) counters)

qqq02:02:47

oh right, i see it now

qqq02:02:02

click-callback (fn [id] (om/transact! this `[(counter/inc ....

noonian02:02:00

The actual increment function implementation is defined near the beginning: (defn increment-counter [counter] (update counter :counter/n inc))

qqq03:02:03

where do I get untangled-devguide.tutmacros from ?

noonian03:02:41

Its in the repo for the devguide: https://github.com/untangled-web/untangled-devguide/blob/master/src/devguide/untangled_devguide/tutmacros.clj Just a single macro and its specific to devcards.

qqq03:02:59

(:require [....
[untangled.client.data-fetch :as df]])
^^ this causes a a cljs analyzer error

qqq04:02:26

woot, got QuickTour example working

qqq04:02:34

What does "static" mean in the context of defui ?

qqq06:02:40

devcards / untangled / :history / :insert-data is amazing

qqq06:02:06

in (dom/... ) how do I generate a checkbox?

qqq06:02:13

I tried (dom/input {:type "checkbox"})

urbank08:02:21

@qqq I think you have to say (dom/input #js {:type "checkbox"}) so that the dom attributes is a javascript object

qqq08:02:53

@urbank: ah right, thanks!js could really give me a more helpful debug msg than a 20-frame deep react stackframe

qqq08:02:34

btw, the untangled devguide exercises are awesome

qqq08:02:44

whoever wrote that guide deserves to have their salary doubled

qqq08:02:56

@adambros: is that useable with om/untangle? I actually like the reframe style hiccup more than om/untangle's dom thingly,

adambrosio08:02:15

afaik the only reason we dont use it in untangled is tony is against exporting extraneous dependencies

adambrosio08:02:38

imo we should just all be using it, but i’m not in charge 😅

qqq08:02:44

you also like hiccup style [:ul [:li ...] [:li ...}} more than om style (dom/ul nil (dom/li nil ...) (dom/li nil ...) ?

qqq08:02:20

you know, the biggest obstacle with me working through the guide right now is precisely the (dom/ul, dom/span, dom/li) stuff

qqq08:02:29

what is a good guide on using sablano / hiccup / reframe style gui in untangle?

qqq08:02:37

this would drastically incease my productivity

adambrosio08:02:43

i dont think there is one, but it should be very easy to use

adambrosio08:02:07

iirc when i tried using it you just have to wrap each render method with sablono.core/html

qqq08:02:27

I'm being greedy here now

qqq08:02:37

Is ther a way to use https://github.com/Day8/re-com with untangled?

qqq08:02:41

that'd complete my stack

adambrosio08:02:30

it covers using react components in untangled, should at the very least point you in the right direction

qqq09:02:38

i'm surprised untangled isn't more well known

qqq09:02:54

I suspect part of the problem is that it requires cljs/clj on client/server, and at that point, many ppl have rolled their own framework

qqq10:02:19

how do I solve this line:

qqq10:02:29

in particular, how do I pass the "onDelete" into the people object ?

urbank10:02:27

@qqq Do you know about om/computed ? I think you have to call om/computed on the props that you pass to the child component. (your-child-component (om/computed props {:onDelete ...})) and use om/get-computed on the props of the child component within its render method.

urbank10:02:19

'props' in the call to om/computed being the props that you're sending the component anyway

qqq10:02:58

@urbank: yeah, looking at another exampke, looks like I have to use om/computed

qqq10:02:10

let me try some stuff out, it looks like thing that is hairy to explain, but easy to get to work

urbank10:02:13

I think it's quite elegant, because it keeps the callbacks and such separate from the actual data the component needs to display.

qqq10:02:12

I think I should write 10k untangled loc before responding to that 🙂

qqq10:02:43

I can assure you taht my current untangled code is NOT elegant by any means, but it's mostly due to my n00biness

qqq10:02:41

alright, I finished devguide-B onto devguide-C now

qqq10:02:55

reading the first paragraph -- does untangled purposely choose it's own format, and thus datascript can't be used as the db?

qqq11:02:29

hmm, so the entire untangled method is to use tables to denormalize and to make sharing explicit ?

urbank11:02:45

Haha, yeah I'm just learning it as well. I just like the idea that there's a box you put stuff like :onDelete in that separate from data such as :person/name . I think it's mostly an aesthetic preference 🙂

urbank11:02:47

Don't really feel qualified to answer the last two question though...

qqq11:02:11

opinion is fine, at this hour, my choices are : someone else's intuition vs nothing

urbank11:02:22

Ok, though someone will hopefully correct me if I mislead you. I think untangled has a preset format because it enables a lot of cool automatizations. So I don't think it's meant to be used with datascript. Perhaps it's possible to hack it together somehow, but that might defeat the purpose

qqq11:02:30

but I don't understand it well enough yet

qqq11:02:49

this is so exciting though; it's like learning programming for the first time

qqq11:02:06

it's like going from qbasic to scheme, when you realize you have been doing all this wrong all this time

urbank11:02:52

Yeah, it is exciting!

qqq11:02:59

okay, I get it now it's definitely NOT datascript compatible

qqq11:02:29

all data in untangled db is of the following format, it has a "2-level key" and the value

qqq11:02:40

the key is of the format [keyword id], where keyword has to be a keyword, and id can be anything

qqq11:02:59

so the [keyword id] "pair" serves as the "key" (called an "ident")

qqq11:02:20

the db is a bunch of (ident, value) pairs, though it's stored as a 2-level table indexed first on keyword, then on id

qqq11:02:35

then there's somet function which takes this "tree" and converts it into a "db" (if you have shared components)

qqq11:02:43

this solves the "diamond" problem mentioned ealier in the devguide

urbank11:02:20

The one thing I'm confused by is if every [:keyword id] in the db is necessarily an [Ident id] value.

qqq11:02:38

I don't understand you rquestion.

qqq11:02:49

type ident = vector of length 2, where first elem = keyword, second elem = anything

urbank11:02:00

right. so [:keyword id] is in ident. But what if the db stores something which is a [:keyword some-value], but it shouldn't be treated as an Ident

urbank11:02:14

I mean, I'm not sure if that would even come up, but still

qqq11:02:26

I think the answer is: don't do that

qqq11:02:34

the way I think about this is as follows:

qqq11:02:37

keyword = maps to the name of the table

qqq11:02:47

id = maps to the "key" in the table

qqq11:02:58

this an ident = [keyword id] = saying in table XYZ, item ABC

qqq11:02:19

^^ this may be entirely wrong but it's my urrent mental model

qqq11:02:04

this also maps amazingly well to gae/datastore, so I'm very happy

urbank11:02:01

Yeah, I think that's the right way to look at it.. and "don't do that" probably is the answer with regards to storing stuff that just looks like idents 🙂

qqq11:02:48

you can store stuff that looks like idents

qqq11:02:54

they get resolved in the normalization process

qqq11:02:18

for example, section "Everything in Tables"

{ :lists/by-category { :friends { :list/id :friends :people [ [:people/by-id 1] [:people/by-id 2] ] }
                       :enemies { :list/id :enemies :people [ [:people/by-id 5] [:people/by-id 9] ] }}
  :people/by-id { 1 { :db/id 1 :person/name "Joe" :person/mate [:people/by-id 2]}
                  2 { :db/id 2 :person/name "Sally" :person/mate [:people/by-id 1]} ... }}

qqq11:02:38

i'm happy to hash this out, because I think we each understand 80% of what's going on

qqq11:02:44

and this gives us a chance to debug each other's understanding

urbank11:02:58

Yes, I think that's correct. I was talking about storing stuff that looks like an Ident but isn't, and so you don't want it to get resolved

qqq11:02:40

hmm; I haven't considered that

qqq11:02:02

so your point is, suppose we ahve an indent [:programmer "Rich-Hickey"]

qqq11:02:18

but then suppose we wanted to store the data tuple [:programmer "Rich-Hickey"] -- and how do we tell it to not resolve it?

urbank11:02:47

Yeah, basically. Not sure when you'd want to do that. I'm more wondering whether untangled would try to treat it as an ident

qqq11:02:14

this is a great question

qqq11:02:48

I don't think it's answered in Section C exercises; when I get to it in seciton D, E, F, or G, I'll let you know 🙂

urbank11:02:32

Ok, thanks! 🙂

qqq11:02:53

actually, I lied

qqq11:02:02

if you look at section 3, exercise 2, it does the ident thing

qqq11:02:09

basically, the trick appears to be "you can resolve ident manually"

qqq11:02:32

and thus, when you get [:programmer "Rich Hickey"] you can decide whether you want it as a tuple or to look it up, @urbank

urbank11:02:23

Ah, that makes a lot of sense!

qqq11:02:27

want to help me solve exercise 3?

qqq11:02:37

I'm not quite sure what's going on, but I think this om/db->tree is related to what we're talking about

urbank11:02:04

Ok, i'll open the page

qqq11:02:43

bottom of that links to the db exercise, 1 & 2 are trivial, 3 I still don't understand yet

urbank11:02:00

right, I have it

urbank11:02:22

Aha, so that's basically the thing that resolves the queries.

qqq11:02:54

okay, I got it working

qqq11:02:29

the answer is:

(def c-ex3-uidb
{:main-panel {:toolbar ...} {:canvas ...}})
where the two ... are filled by (1) wirintg in 0, (2) looking at the error on why it doesn't match nd (3) copying/pasting the answer it's supposed to be

urbank11:02:13

Aha, so you had to make a db that given that query would return that tree.

qqq11:02:28

something like that, I'm not exactly sure, the docs confuse me

qqq11:02:50

like wtf does

db->tree

(om.next/db->tree query some-data app-state-db)
Given a query expression, some data in the default database format, some application state data in the default database format, denormalize it. This will replace all ident link nodes with their actual data recursively. This is useful in parse in order to avoid manually joining in nested relationships.
mean, and why is ex3-uidb passed twice ?

urbank11:02:20

btw, playing around with om/db->tree I found out that you can use [*] inside a query to get all the attributes of that object.

qqq11:02:20

alright, I need to sleep

qqq11:02:32

this was helpful; thanks for the discussion

urbank11:02:09

I think that an om thing... db->tree needs it twice, probably they can be different. not sure. Ok goodnight! It was helpful to me to 🙂

tony.kay21:02:14

@qqq @urbank On om/computed: The issue is that Om can re-render a component by running the query for the component's data. When it does that, it calls render without going from root (through the parent)...but the query cannot figure out what the parent wants as callbacks (computed stuff of the parent).

tony.kay21:02:47

By using computed, you're giving Om a chance to cache the last computed values from the last render that went through parent

tony.kay21:02:01

without computed it would work on the first render, but localized refresh would break

tony.kay21:02:14

and urbank is right: the db->tree API covers more advanced cases. The two args are in the same format, but need not be the same database. Most uses will just pass the complete app state both times.

qqq21:02:09

@tony.kay: 1) thanks for creating untangled 2) thanks for occasionally dropping down from mount sinai to answer basic level questions 🙂

qqq21:02:39

one thing I don't get about queries:

qqq21:02:04

is the root query supposed to get EVERYTHING or is it supposed to leave some unresolved for the sub-parts to load on demand?

qqq21:02:04

concretely, in http://untangled-web.github.io/untangled/guide.html#!/untangled_devguide.E_UI_Queries_and_State_Exercises here, I am conflicted over the following: should the root element load the mate of the person too -- or should the root leave that unresolved as a [:db/id NUMBER], and ahve the sub component load it on demand?

tony.kay21:02:42

So, the root query asks for everything. How much you want in RAM right now and what you want to demand load is totally up to you

tony.kay21:02:02

and the UI only cares in the sense of triggering the load via some interaction you define

qqq21:02:18

@tony.kay: suppose I have a root node that has 12 tabs; where only one of the 12 tabs is visible at any given time in the untangled model, do I load the data for all 12 tabs at the root, or only the visible one?

cjmurphy21:02:06

@qqq: In that case you could use a union query and only one of the 12 would be loaded at any one time.

tony.kay21:02:06

what @cjmurphy said...but remember that there are 2 concerns: What the UI query processes from RAM -> UI, and what you've loaded from a server. The cookbook shows an example of a tab triggering a network load.

tony.kay21:02:07

all up to you

tony.kay21:02:03

the cool think is you can hide the remote trigger for a load from the UI logic. The mutation to navigate just composes in a load

tony.kay21:02:32

the UI just says "go to tab 5". the mutation looks in RAM and notices..."Oh, don't have that yet...load it"

tony.kay21:02:45

it is just what you'd hope for: complete flexibility

tony.kay21:02:16

with the capability of not tying model/logic/app behavior up in the UI itself

qqq21:02:48

here's another extreme case:

qqq21:02:39

I have a two panel setup. Left panel = 1 MB of data to render. Right panel = 12 tabs, each of which takes 100kb of data to render. Now, suppose I'm loading the right lazily. Then, when I switch a tab in the right, is the left reloaded (a 1MB data cost) -- or, if it's not -- how does it know the data is not stale?

tony.kay21:02:03

cache invalidation is your problem (tm) 😉

tony.kay21:02:21

reloading is completely under your control.

tony.kay21:02:28

it never ever happens unless you tell it to

qqq21:02:46

sorry, let me rephrase my confusion

qqq21:02:04

If "root node is in charge of loading ALL data", then when I switch a tab, wouldn't it automatically relaod the left panel 1MB also ?

tony.kay21:02:38

so, there is a diff between loading (a server interaction) and providing the current client-side app state to the UI

tony.kay21:02:48

just a min...

tony.kay22:02:05

the UI query is about moving data from RAM onto the screen. Union queries, mentioned before, allow you to limit what goes from RAM towards the UI to one branch at a time.

tony.kay22:02:23

The query on the UI is not directly sent to a server, ever

tony.kay22:02:35

you decide what to ask the server for as part of mutations

tony.kay22:02:52

The fact that you can use a portion of the UI query to solve that is part of the data-driven story

tony.kay22:02:06

but don't mistake the total UI query for a server query

tony.kay22:02:46

the UI concern (union queries) is purely a rendering efficiency concern.

tony.kay22:02:09

The server interaction concern is addressed by the Untangled data-fetch namespace functions, like load, which you compose into mutations

tony.kay22:02:39

Using a Person UI component in a load just joins together what that UI component wants with what the server can provide

urbank22:02:36

Hm... so if I'm not missing something, I think something goes awry with queries if I use [org.clojure/clojurescript "1.9.473"] with untangled, whereas it works fine with [org.clojure/clojurescript "1.9.229"]

urbank22:02:30

if I do (om/get-query Component) with 473 I get nil back, but I get the actual query with 229

urbank22:02:02

So I suppose this looks like an om issue

qqq22:02:52

(ns app.core
  (:require
    app.mutations ; remember to load your add-on mutations
    [untangled.client.core :as uc]
    [app.ui :as ui]
    yahoo.intl-messageformat-with-locales ; if using i18n
    [om.next :as om]))

(defonce app (atom (uc/new-untangled-client :initial-state { :some-data 42 })))
(reset! app (core/mount @app ui/Root "app"))
http://untangled-web.github.io/untangled/guide.html#!/untangled_devguide.F_Untangled_Client where does core/mount come from?

adambrosio22:02:06

that should be uc/mount

qqq22:02:17

(defui ^:once Root
   static om/IQuery
   (query [this] [:ui/react-key ...])
   Object
   (render [this]
      (let [{:keys [ui/react-key ...]} (om/props this)]
        (dom/div #js { :key react-key } ...))))
what does the ... mean ? this is in http://untangled-web.github.io/untangled/guide.html#!/untangled_devguide.F_Untangled_DevEnv

tony.kay22:02:47

In this case I'm just saying "whatever else"

tony.kay22:02:51

it is pseudocode

tony.kay22:02:02

it isn't meant to be literal code

tony.kay22:02:21

sorry bout that. ... in a proper query would mean recursion

tony.kay22:02:42

In that case I'm just trying to focus your attention on react-key and nothing else

qqq22:02:06

@tony.kay: got it; thanks!

tony.kay23:02:32

@urbank the version of cljs and Om can be closely tied (e.g. latest + latest is often the case). Om, being a project with close ties to the maintainers of cljs, tends to push the envelope

qqq23:02:20

@tony.kay : is there any reason idents are exactly 2 levels of a (keyword, id) pair? it seems that if it was "just one level", it'd basically be a virtual pointer and at more than 2 levels, I'm not sure what it is but why was 2 the right number?

tony.kay23:02:22

because the database format:

{ :table { id { entity }}}
(get-in db [:table id]) <--- thus an ident...the identity of a table entry

tony.kay23:02:35

everything normalizes into tables. Their ident is their location

qqq23:02:52

so the db really is meant to be A GROUP OF TABLES, where each TABLE is a bunch of (id, entity) pairs ?

qqq23:02:57

thus the 2 levels?

tony.kay23:02:59

yep...flat. The structure is made by idents linking things together