Fork me on GitHub

Just uploaded my talk from a meetup in Portland to YouTube. Still processing at the moment, but should be visible soon:;


nice, thanks for posting simple_smile


how do untangled deal with staled data in remote fetch. For example I have the old data in :project/search, now I do another search ( fetch ) with different params, and of course I don't want to fetch the old one in the state, I want to check if the params is the same with the one before, then load it and at the same time we do the remote fetch, otherwise just return nil and remote fetch.


Just from that talk. set-query! and IQueryParams are not used in UnTangled - is that correct?


yeah it is


I am watching the talk as well


I wonder if Navis have found any use for subquery??


@tony.kay: Great video! Thanks! 😀


@tony.kay: nice Untangled T-shirt


@anmonteiro: Thanks! Our UI guy made the logo...custom T-shirts are so easy to get these days. Notice the sticker on my laptop? @jagare: Thanks, hope it was helpful. @nxqd: stale data is detected by you, of course. You have to write the logic. If you send a load (remote query) you can include parameters that let the server detect staleness. Too many possibilities to be very specific, but one example: You run a query with some top-level key that is different from your stale data. If you get something back use a post-mutation to move the new stuff into place. If you don't...well, don't. If you're asking about read after write (e.g. did a mutation, and want to do a follow-on read: use the built-in (app/load) mutation (see the implementation of load-data). @cjmurphy: No, that is incorrect. Query parameters (which are known as dynamic queries) are usable. Using parameters in the query syntax that need parser interpretation is what we don't support (since you don't write a parser). [(:prop {:trim true})] is not ok. If you want to say [:a :b ?other-prop-to-choose] and use a query parameter to fill that in, well, that's fine. Of course, sending parameters to the server is not only supported, but encouraged. You'll often want to do that, and you do write a parser on the server.


Some of us tried to re-write the Auto-complete example by moving the component away from the root level. It uses dynamic query (`set-query` and IQueryParams) for the search String that gets sent to the endpoint. The only way to do it was to have a complicated recursive read that had a special pathway for the remote case. With Untangled not having a parser on the client our solution is ruled out. I'm thinking that solving the same problem with Untangled might not call for the use of dynamic queries in the first place...


I have a very form heavy UI with many auto-complete/combobox widgets that require dynamic queries and ui-local state. I see that untangled supports both, but I’m curious having only written a little bit of om-next, is it going to be difficult to create generic components like this? For instance, a combobox requires a lot of ui local state to describe whether it’s open, what is selected, etc. When you create a generic combobox component you’ve got to make sure that each instance has a separate set of ui local state. I realize dynamic queries are unrelated to ui local state, and I ask about them only because it was difficult to compose dynamic queries up to the root in


@cjmurphy: I'd have to think through it to be, the auto-complete bits are essentially: - Grab some characters in the input field (de-bounced collection) - After some amount of typing, send off a query to the server for search results - When search results arrive, put them in place in the database so UI sees them - Write the UI to render properly In Untangled, none of that requires dynamic queries (or component local state for that matter)


- Gathering the characters is a mutation that gathers into the local app state, and sets/resets a timeout - The timeout can trigger a load-data with a post-mutation. The post-mutation can rewrite the search result into the UI portion of the database, and also update :ui attributes - The various other states (combo box open) can be done with :ui attributes, which puts them in the app state and makes the support viewer usable - Rendering should be a pure function with no local state...a given input state shows a very specific rendering.


I would suggest that your state-of-the-widget be something that is nicely denormalized...e.g. no :ui attribute if you can derive what the UI should do from the data itself. A concrete example: "all checked" is not something you store as a boolean anywhere for a list of checked things...instead you derive it from the items themselves. Thus, you state cannot possibly be wrong.


(let [all-checked (every checked? items)] ...)


Also, don't use the Om default of local state for form the state of a form element to declared data


that way the UI cannot be out-of-sync with app state


yeah, I stay away from component local state for that reason


This also has the advantage that you can unit-test your logic around your UI rendering without actually using the UI


if the data is right, your UI is right


however i still need a generic way to, say, indicate that this combobox is open and this item is selected or hovered, and not another combobox elsewhere. Do have a recommendation on how to achieve that


or does every instance of such a widget need its own defui


The box queries for :ui/open and updates it with built-in mutation: (m/toggle! this :ui/open) The items do similar...just add in :ui prefixed attributes


(m/set-value! ...) for strings on ui attributes


the :ui prefix ensures those attrs never appear in server interactions if you use those UI queries on a load-data


Yes, they need they need a defui


And remember that it is perfectly fine to use defui JUST to make queries (no render)


oh? I hadn’t thought about that


(defui ServerQuery static om/IQuery (query [this] ...))


You'll want Idents as well, to get normalization


That is a standard acceptable Om practice


I was about to ask, then, if it’s not possible to create a generic Combobox/autocomplete that get’s used in different places across that app


such a thing is possible simply by not defining the render?


certainly...just make a "constructor" method that sets up the app state, and pepper calls to that about in the initial app state (or as result of a mutation to put one up)

tony.kay17:04:15 mean re-use the state of a single on in multiple places? Yeah, that is ok too


as long as they aren't on screen at same time


just use a link query, and put the state at the top of the database


they are on screen at the same time. many autocompletes in the same screen


they all have the same basic structure, but query different things


`[ {[:combo-box _] (om/get-query ComboBox)} ]


ok, so the other answer


multiple ones...just make a constructor that makes the correct app state, use that in initial app state or at mutation that initializes a form


you could also give the combo boxes global identities, and put them in a top-level initial app state table:


(def initial-app-state { :combobox { :search-box (cb/make-combo-box) :email-box (cb/make-combo-box) ...} })


then the query can join on idents: (defui ... (query [this] [{ [:combobox :email-box] (om/get-query ComboBox)}]))


yeah, that sounds nice. Just so I’m clear, when you say constructor is that the function resulting from calling om/factory on the class?


of course then the ComboBox Ident function returns [:combo-box (:boxid this)]


no, I mean a constructor to be a function that returns a map of what state you need to track


(defn make-combo-box [id otherthings] {:boxid id ...})


just convenience for clarity when generating app state


instead of hand-coding the stupid map every time


also gives you a true "component" feel....the state is defined with the defui via this constructor


You can think "I put a combo box here in the app state, so then I can query it there, and render it there"


well, render it anywhere if it is in a table, since you can join on idents


well, thanks. that’s a good amount for me to get my head around. I’m still not clear on how idents work, so I’ll try to get something like [ {[:combo-box _] (om/get-query ComboBox)} ] working. Do you know of any example projects that use a similar approach?


I clarified the defs above


All an Ident is, is a location in an app state database table


So you construct initial app state already normalized, since you are using functions to do it?


[:tbl 1] is used just like a get-in paramter against your app state


so a join: {[:a 1] [:x :y]} means "get :x and :y from the object I see with (get-in [:a 1] app-state)


Om happens to also know how to take a tree and turn it into tables (reversing this operation for incoming tree data)


The declaration of Ident on the ui component (which is also where you get the queries) just defines what database tables hold the data for what UI components


you make the table names and IDs up...the table names are required to be keywords


@cjmurphy: You can do it either way, if your UI matches up....I often find hand normalized app database is easier to deal with. In our production apps, that's mostly what we do.


you can also mix and match!


ah, I think see how using a link works nicely for what would be component local state for a generic component


@cjmurphy: just use merge (or maybe deep-merge) on hand-normalized combined with a tree you're running through tree->'s all just data simple_smile


@uwo Right...but even that instance of the component (data) could be rendered in multiple places...not very useful on screen at same time, but nice for reusing state in different dialogs, for example...just query for the same ident in two different dialog UIs.


ah, gotcha


@uwo That also gets you "persistence"...e.g. the multiple on-screen uses remember the last thing you did simple_smile (since they share the data state)


which can be desirable


imagine you're always asking a similar question they might answer the same next time you ask it


but it is in a diff place in the UI


Hand normalizing - maybe easier to have a dedicated app that does that for you, giving you the thumbs up when you've got it right.


@cjmurphy: Like I said...use tree->db for the parts that match the UI, and hand normalize anything that might not normalize right...e.g. union queries are a bit problem...anything that isn't currently on the screen won't auto-normalize.


so, initial app state can either be generated by multiple bits of state that you combine applying tree->db, or just hand normalize it....which in practice is really pretty simple.


Oh thanks - I'm used to everything being able to normalize.


yeah, in doesn't simple_smile


Unions cut out huge swaths of "things not on the screen". A tree can only contain one version of a branch at a time


meaning you cannot even represent "tabs" via a union in a tree


Also, you might want to encode on UI bit that represents a pop-up, and use a union to populate it...again, not going to be possible to give an initial state with auto-normalize


So union is a good way to handle tabs..


That's how I do it. See untangled-demo


Also makes it possible to have a hot-loaded (from server) tab


Yes cool, looking forward to reading it...


You see the Clojure PDX talk I posted earlier?


That little demo program I show with the hot-loaded comments is what I'm referring is like 150 lines of code


very digestable, and full-stack


Yes - and that's on github?


yep...might be under awkay instead of untangled


OK....gotta get on the road to Seattle...headed to clj west. I'll be wearing an Untangled T-shirt. Anyone: feel free to say "Hi". I'm there to promote simple_smile


thanks again.


@tony.kay: I noticed in todo-mvc you used local component state to track the value of an input field. Is that just because the code was adapted from React todo-mvc or do you recommend not tracking input field content in the global app state?


@currentoor: I wrote that piece of the app, I did it that way because I didn’t want to persist edited text to the app state


if you hit the escape key, you need to restore the item’s original text


I could have also saved edits to the text in a key like :ui/edited-text, using the pattern tony just described


basically, I just didn’t want to overwrite the existing todo item text until the user hit enter


@ethangracer: cool thanks. I think I'd prefer to store most things in app-state but in the past I've had flicker issues with input text fields and state tracking.


huh, don’t think I’ve tried using app-state instead of component local state. the downside of using app state is having to write the post-mutation to clear out the ui keywords you don’t want anymore. though i guess there’s no harm in leaving them there either. not sure about the flicker


hello, how can I set the :id-key from the reconciler into an Untangled app?


@wilkerlucio: not understanding your question, are you referring to untangled’s ui/react-key? or a config option in om’s reconciler? I don’t see any config options in the om code for id-key.


nvm, just found it


I don’t think we currently have an option to override reconciler configuration


we probably should though