Fork me on GitHub

@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.


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


(let [{:keys [text type]} (om/props this)]
      (localize-classnames Alert
        (dom/div #js {:class [:alert type]
                      :key 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?


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)


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


and I think it might matter


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


@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!


and (str "alert " type)


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


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.


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


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


perhaps I am misunderstanding your question as well 😕


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.


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]])


@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.


but at least you have the conceptual structure


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


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


mostly in om next.


how would untangled handle it?


I'm re-looking at your example.


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


that would just be a normal function call


to set the initial query params


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.


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


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


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


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


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


query params just cause more complications to reason about


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


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.


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


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


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


esp the getting started videos


at least try the first one


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


awesome. I'll start there then. thanks.


There is also an interactive dev guide, cookbook recipes, ref guide, talks etc at


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


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


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


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


lots of options


@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


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


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.


@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?


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?


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


@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.


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


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


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


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


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


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.


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 you still have a "when do I refresh it" problem. IMHO, better just to start with the caching and refresh, and eliminate the parsing.


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)


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


i gather it's always cached


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)


I'll make a video ASAP 😜


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


correct me if I'm wrong, (just trying to solidify my understanding), the 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)


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


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


and maybe just on the client… ?


hm right, yah my explanation tradeoffs isn't right


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


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