untangled

NEW CHANNEL: #fulcro
tony.kay 2017-03-10T01:27:33.391436Z

@cqowsy So, queries don't exist in isolation. They need a component (e.g. an ident function for normalization) and a database with all the graph edges connected. Untangled uses Om's db->tree to fulfill queries, so if you're seeing a problem it is mostly likely your graph, but otherwise it is an Om bug.

urbank 2017-03-10T15:17:58.684297Z

In InitialAppState if I do (map #(initial-state Component %) [...] ), where Component has an Ident, it doesn't seem to build a table/normalize the data associated with that component, whereas if I inline an equivalent vector, it does the correct thing

kwladyka 2017-03-10T15:19:56.717471Z

https://github.com/untangled-web/om-css/blob/e150b2f1b1171736200abb02fbf3e555054cbca0/src/om_css/core.cljc#L114

(let [{:keys [text type]} (om/props this)]
      (localize-classnames Alert
        (dom/div #js {:class [:alert type]
                      :key text}
                 text)))
issue: div get class alert and type, but i want it to get the class under the type. I tried to solve it in a few way but i failed. How do you make dynamic class name?

tony.kay 2017-03-10T15:55:58.333204Z

@urbank try mapv

tony.kay 2017-03-10T15:56:28.342123Z

oh, and for server-side rendering compat, use get-initial-state instead of calling the protocol on the class directly (you cannot implement protocols on classes in Java)

tony.kay 2017-03-10T15:57:16.355928Z

The to-many case in the app db format uses vectors

tony.kay 2017-03-10T15:57:26.358643Z

and I think it might matter

tony.kay 2017-03-10T15:58:21.374872Z

@kwladyka that is a React-ism: use :className

urbank 2017-03-10T15:58:42.381024Z

@tony.kay Yeah, I had actually tried that. I didn't work. Just noticed that there's was a (map) a level deeper also. πŸ˜… Thanks!

tony.kay 2017-03-10T15:58:48.382637Z

and (str "alert " type)

tony.kay 2017-03-10T15:59:22.392893Z

OH, @kwladyka sorry, I didn't see you using the Om CSS stuff

tony.kay 2017-03-10T16:02:40.453958Z

I don't remember the API very well for that. It was written as an experiment and I don't (yet) use it. If type is a string, I don't think it will work. It is using garden underneath, which uses keywords.

tony.kay 2017-03-10T16:04:21.483168Z

just looked at the source...should work with strings

tony.kay 2017-03-10T16:04:49.491369Z

perhaps you have a problem higher up in code? Might want to check the content of type with a println

tony.kay 2017-03-10T16:07:36.541251Z

perhaps I am misunderstanding your question as well πŸ˜•

urbank 2017-03-10T16:15:16.671169Z

Any opinions on using Untangled without clojure on the server? I guess I still have the client-side benefits, but there'll have to be some more glue on the seams.

puppybits 2017-03-10T16:34:56.018388Z

Is there a way to use a key in the state for the query? I have multiple components that can set the date and one that needs to read the active date and filter from a list to display the active date. Am I able to reference a key as the value of a query? I currently have a query param but want the value to come from the reconciler.

static om/IQueryParams
  (params [_]
    {:active-date (t/today-at-midnight)}) <-- how to get this to read from the reconciler?

  static om/IQuery
  (query [_]
    '[[:budget/by-date ?active-date]])

tony.kay 2017-03-10T16:55:00.355510Z

@urbank You can do it. Lots of choices to make. You lose a lot of simplicity for the server. Other than that, it is a very wide question. What alt lang? Transit or raw JSON? etc etc etc. You're inventing half of the stuff Untangled gets out of your way.

tony.kay 2017-03-10T16:55:20.361187Z

but at least you have the conceptual structure

tony.kay 2017-03-10T16:55:40.367516Z

transit is widely supported. You'd have to parse/process your own queries from Om syntax

tony.kay 2017-03-10T16:56:15.377899Z

@puppybits You're working from an Om Next mental model. In Untangled, that is not how you do it.

puppybits 2017-03-10T16:56:35.384354Z

mostly in om next.

puppybits 2017-03-10T16:56:42.386385Z

how would untangled handle it?

tony.kay 2017-03-10T16:59:07.427807Z

I'm re-looking at your example.

tony.kay 2017-03-10T16:59:42.437893Z

actually that should work in the way I understand it. The reconciler is not involved in the read where you've commented

tony.kay 2017-03-10T16:59:48.439900Z

that would just be a normal function call

tony.kay 2017-03-10T16:59:52.441056Z

to set the initial query params

tony.kay 2017-03-10T17:00:55.461749Z

In Untangled, I'd have a prop in the app state for which active-date I was using, and I'd set that. The graph edge in the database to the "report of interest" would be part of the graph as well.

puppybits 2017-03-10T17:00:56.462015Z

it does work but the active-date value needs to come from the reconciler.

tony.kay 2017-03-10T17:01:23.470332Z

that statement doesn't compute for me...but I don't normally use query params, as I said

tony.kay 2017-03-10T17:01:37.475007Z

If you are mostly using Om Next, you should prob ask them in #om

tony.kay 2017-03-10T17:02:29.490907Z

The Untangled approach is to represent things in the database...`:current-report` would just point to the report, and mutations would move that around to point to the "active" one

puppybits 2017-03-10T17:02:31.491216Z

ok. I'm just looking into different options for client-side apps. It looked like untangled is using a lot of om.

tony.kay 2017-03-10T17:02:45.495679Z

query params just cause more complications to reason about

tony.kay 2017-03-10T17:02:50.497088Z

IMHO

tony.kay 2017-03-10T17:03:18.505210Z

Untangled encourages use of a much smaller subset Om Next features.

tony.kay 2017-03-10T17:03:55.516225Z

You don't write parsers on the client, tend to avoid query params on the client (ok for server comms), use the default db format, you don't have to write merge logic, etc.

tony.kay 2017-03-10T17:04:47.532039Z

So, a better question for this channel might be "How do I do X in Untangled?" as an abstraction...like, "How do I move from day to day when showing reports in my UI?"

tony.kay 2017-03-10T17:05:07.537593Z

that sort of thing...but then you're going to get directed to various video tutorials and docs 😜

tony.kay 2017-03-10T17:05:23.541974Z

There is a ton of tutorial material available. I highly recommend it.

tony.kay 2017-03-10T17:05:30.543842Z

esp the getting started videos

tony.kay 2017-03-10T17:05:35.545349Z

at least try the first one

tony.kay 2017-03-10T17:07:01.569103Z

start with OVerall Project Structure (5 mins) and then do Base UI and State (22 mins)

puppybits 2017-03-10T17:07:15.572913Z

awesome. I'll start there then. thanks.

tony.kay 2017-03-10T17:07:19.574292Z

welcome

tony.kay 2017-03-10T17:08:06.586558Z

There is also an interactive dev guide, cookbook recipes, ref guide, talks etc at https://untangled-web.github.io/untangled/docs.html

urbank 2017-03-10T17:16:14.714895Z

@tony.kay I have neglected reading about the server-side of Untangled, I'll look into it. Probably more questions on the way πŸ™‚

tony.kay 2017-03-10T17:19:03.760975Z

@urbank I thing the ref guide on networking is possibly a good place to start

tony.kay 2017-03-10T17:20:22.782809Z

mostly you'd need to supply the API (reads + mutations) in the correct form on your server, hopefully via transit

tony.kay 2017-03-10T17:20:43.788240Z

You can also replace the networking in client, talk to whatever you want, and restructure it to cljs data structures on the client side

tony.kay 2017-03-10T17:20:48.789462Z

lots of options

wilkerlucio 2017-03-10T18:11:11.589714Z

@urbank the repository I sent you before with Untangled code, that project uses Node.js with Clojurescript for the server, you might find something useful for you there, if you want to use something non-clojure at all (let's say Ruby), then you would have more work to do, because to parse the server you would have to parse the query yourself (while in Clojure/Clojurescript world om provides om/query->ast), but it's doable

wilkerlucio 2017-03-10T18:12:22.608180Z

another option is to code a completely different network provider, that could parse the query on the client and trigger some REST endpoints or anything else (like use a local database for example), really, lot's of points to do your implementation, your imagination is the limit, hehe

urbank 2017-03-10T18:18:46.706268Z

Yeah, it's probably going to be python, because the server is already running that, and I don't see much interest in a rewrite on that side πŸ™‚ . So mostly a client-side reboot. My initial assumption was that I would be translating queries into requests for the REST api.

urbank 2017-03-10T21:00:19.164150Z

@tony.kay You mention that links ([:something/by-id '_]) should not be overused. However, for the Todo Task Counter example you recommend (?) that the Counter take the table and count the entries. Do you have a rule of thumb for this? Or do you think that even the Counter ought to just query the todo-list?

urbank 2017-03-10T21:02:51.200490Z

For example, if there are a bunch of multiselects on page that select from items that are in a table. So if a new item is added, the multiselects have an additional option. Is that a good use case for links?

urbank 2017-03-10T21:03:45.213201Z

I suppose, like in the todo app, the things in the table could also be kept in a list in the db, and the multiselects could query that. Though I suppose that's much the same thing

tony.kay 2017-03-10T21:48:05.805305Z

@urbank My generally recommendation is to make the graph and properties in the DB represent the "state of the world right now". In some cases "denormalizing" derivable facts is fine. For a count you have some choices: count them whenever you change the table and plug that into a property in the graph node that cares (shows the count). Make a top-level prop, and store the count there , and query it with a link. Or, query for the table, and do your count in the UI logic. Thing is, usually any given "view" of data is a derived view. You might have 10 todo lists in memory, and are only showing one. When you query for the list, you can count the items. That is always true. It doesn't require link queries. It is an alternate view of a list, where you query for the items, but not much item. The graph can represent that derivation (grouping items into their lists), or derive it on the fly.

tony.kay 2017-03-10T21:48:29.810982Z

so, in my example I don't recommend it take the whole table (if that's how it came across I misspoke)

tony.kay 2017-03-10T21:49:04.819023Z

I might use the whole table to derive data in a mutation that is updating a derived fact about the table.

tony.kay 2017-03-10T21:49:28.824079Z

For a pure count of everything in a table, you could query for the table, but that isn't a very good thing to do in general

tony.kay 2017-03-10T21:50:06.831823Z

Make the graph in your database have the views of the data you need, then update those at well-understood mutation/load boundaries

tony.kay 2017-03-10T21:50:50.841669Z

"people under 20" is a view of the Person table. It involves a filter and conversion into a vector of idents...store that vector as a prop on any node and you have that view of those people.

tony.kay 2017-03-10T21:51:14.846542Z

When you update the table, update the view...or even better: update the view with a mutation AS you put the component on the screen.

tony.kay 2017-03-10T21:54:16.887086Z

I need to make another video...this has come up a lot lately, and it is the primary critical difference in the "general philosophy" of raw Om Next and Untangled. In Om Next you write parser emitters that implement your reads from the db. In those you can "generate the view" on the fly. Problem is, this is a lot more code to write, and due to computation leads you to cache the result in the app state anyway...so you still have a "when do I refresh it" problem. IMHO, better just to start with the caching and refresh, and eliminate the parsing.

tony.kay 2017-03-10T21:55:30.903217Z

it moves the caching logic to the place you are likely to want to think about it: During I/O and when putting the data on the screen (UI routing)

2017-03-10T21:57:17.926021Z

@tony.kay in untangled the "generate the view" computation still exists and is done client side ? or it's done server side?

2017-03-10T21:57:27.928371Z

i gather it's always cached

tony.kay 2017-03-10T21:58:26.941818Z

By "generate the view" I mean add derived graph info to your client db. You can do that either way...but I typically mean as a post mutation from a load, or a composition of concerns into a mutation (view gen + ui routing)

tony.kay 2017-03-10T21:58:37.944151Z

I'll make a video ASAP 😜

urbank 2017-03-10T21:59:21.953965Z

@tony.kay Already a lot clearer from your answer! Looking forward to the video!

2017-03-10T22:04:18.019499Z

correct me if I'm wrong, (just trying to solidify my understanding), the om.next philosophy is a query expression contains all the specifications about what derived view it needs e.g. [:name :phone (:images {:top 3})], and the majority of that work of computing the view is done server side to minimize network usage and send back only what is needed. In untangled you give up query params, so the ability to fully specify a derived view via query expressions is sacrificed (and you gain not having to write query parsers)

2017-03-10T22:05:49.038571Z

The tradeoff is roughly between the complexity of writing query parsers TO network usage, more work done client side.

adambros 2017-03-10T22:06:37.048124Z

you only give up query params when doing reads, if you are doing a load you can still send parameters

adambros 2017-03-10T22:07:07.053990Z

and maybe just on the client… ?

2017-03-10T22:08:22.069456Z

hm right, yah my explanation tradeoffs isn't right

adambros 2017-03-10T22:10:03.090760Z

yeah, you lose parameterized reads on the client, so you instead need to make sure you are keeping your (extra) views updated in app state via mutations

2017-03-10T22:10:07.091535Z

untangled decouples client reads from server reads, the query expressions are different