This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-04-14
Channels
- # admin-announcements (5)
- # aws (3)
- # beginners (35)
- # boot (96)
- # cider (1)
- # clara (6)
- # cljs-dev (12)
- # cljsrn (34)
- # clojure (151)
- # clojure-boston (3)
- # clojure-brasil (4)
- # clojure-canada (1)
- # clojure-czech (8)
- # clojure-dusseldorf (11)
- # clojure-japan (5)
- # clojure-russia (120)
- # clojure-taiwan (1)
- # clojure-uk (3)
- # clojurescript (7)
- # component (27)
- # cursive (13)
- # data-science (45)
- # datomic (1)
- # devcards (5)
- # emacs (3)
- # funcool (65)
- # hoplon (103)
- # instaparse (3)
- # jobs (14)
- # jobs-discuss (1)
- # juxt (2)
- # lein-figwheel (2)
- # off-topic (16)
- # om (20)
- # onyx (49)
- # parinfer (17)
- # perun (1)
- # planck (5)
- # proton (4)
- # re-frame (14)
- # ring-swagger (4)
- # spacemacs (4)
- # untangled (110)
- # yada (14)
Just uploaded my talk from a meetup in Portland to YouTube. Still processing at the moment, but should be visible soon: https://www.youtube.com/watch?v=IeIyQDg9gBc&feature=youtu.be
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?
@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 om.next.
@cjmurphy: I'd have to think through it to be sure...so, 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.
Also, don't use the Om default of local state for form widgets.....pin the state of a form element to declared data
This also has the advantage that you can unit-test your logic around your UI rendering without actually using the UI
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
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
the :ui
prefix ensures those attrs never appear in server interactions if you use those UI queries on a load-data
And remember that it is perfectly fine to use defui
JUST to make queries (no render
)
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
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)
oh....you mean re-use the state of a single on in multiple places? Yeah, that is ok too
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?
no, I mean a constructor to be a function that returns a map of what state you need to track
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, 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?
So you construct initial app state already normalized, since you are using functions to do it?
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
@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.
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->db
...it's all just data
@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.
@uwo That also gets you "persistence"...e.g. the multiple on-screen uses remember the last thing you did (since they share the data state)
imagine you're always asking a similar question they might answer the same next time you ask it
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.
Unions cut out huge swaths of "things not on the screen". A tree can only contain one version of a branch at a time
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
That little demo program I show with the hot-loaded comments is what I'm referring to...it is like 150 lines of code
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
@tony.kay: I noticed in todo-mvc you used local component state to track the value of an input field. https://github.com/untangled-web/untangled-todomvc/blob/master/src%2Fclient%2Funtangled_todomvc%2Fui.cljs#L62 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