Fork me on GitHub

hey all, I have a need to use :ui/... attribute on my root component, and that means I have to specify ident for this root component, so I set up something like this on my Root:

static om/Ident
  (ident [this props] [:root :main])


Shouldn't that mean that map value that I pass to :initial-state will be auto-converted to db state that starts with: {:root {:main ....}} ?


because it wasn't converted to that


After that I tried setting explicitly my own db state by wrapping my map in atom, and manually of course rewritten the map to start with {:root {:main...}} but it still didn't get proper value in render props


am I missing some obvious thing here?


@vmarcinko: you only have to specify an ident on components using ui/… attributes if you want to use the helper functions in untangled.client.mutations. you can write your own mutations that add those keywords to the top level of your app-state, and then query for them from the root component


it doesn’t make sense to have an ident on the root component. What would it’s join point be? In your example, you assign the ident [:root :main], so there would have to be a component above root that queried for [:some-key], where the app state looked like {:some-key [:root :main]}


in which case, the component with :some-key would be the root component. kind of a proof by contradiction in that sense (that the root component can’t have an ident), since we started out by assuming that the component with the [:root :main] ident was the root component


@vmarcinko: Yeah, the root component is a bit tricky. It is easy enough just to nest things down one level and treat root as a special case. It shouldn't be a problem to give Root an ident and normalize it, is just one more level of indirection. What did your state look like? I've honestly not tried.


I've started a Cookbook project: It has a template and a script for creating new recipes. It is mostly empty at the moment (just ideas written down and one recipe mostly written). I'm going to try to add a few a week. If anyone has an idea of how to track interest in specific recipes I'll be glad to write those first.


Also, anyone wanting to contribute: This is a great way.


I am going to be a bit strict about content. Trying to maintain a level of consistency and quality. I'll post guidelines soon.


@tony.kay: would you need a custom read to treat the normalized root as a special case? or is there a way to do it with the built-in read?


@ethangracer: @tony.kay Yes Ethan, I tried to use your set-string! mutation function within root component, and it complained about root having to have ident, so I also was a bit baffled by root comp idents...As you said, I even assumed that I would have to introduce new root component, that would be on top of exsting one due to this path issues


So, that's why I asked here to see if I don't udnerstand something here, or none yet tried to put ident on root, as it seems is the case


Actually, I still am not sure is it normal to use set-string! at :onChange handler for form's text field which I'm trying to do now, and I started by using set-string! Although it doesn't look so nice to me to see logs about transact! being called on each key pressed inside text field


but as I udnerstood, as complete om/untangled noob, that if I use component local state, then text field's value isn't visible in app state, and support viewer will not be able to reflect changes insdie these text fields


thus i decided to try with set-string!


@vmarcinko: ah, gotcha. it sounds like you understand it to me, I haven't been successful getting an app-state to normalize in any reasonable way with a root component ident, though I haven’t given a concerted effort in awhile. as far as what defines 'normal' patterns, we’re still very much in the process of defining it. very open to feedback on what others think are good approaches. you are correct though, component local state is not currently recognized by the support viewer


if you’re wanting to mitigate the logging clutter for set-string!, we found debouncing to work well — still a fair number of logs, but significantly fewer than one per character


(have to learn a lot of terms it seems)


haha, no worries, me too! web development is very new for me


yeah, im mostly server-side dev, so not experienced with JS/UI world


it’s a timeout on a field, so the onChange handler doesn’t trigger for every onChange, but only after the user hasn’t entered something for awhile


(defn debounce
  ([f] (debounce f 1000))
  ([f timeout]
   (let [id (atom nil)]
     (fn [evt]
       (if (not (nil? @id))
         (js/clearTimeout @id))
       (reset! id (js/setTimeout
                    (partial f evt)


and then we use it on a text-field like so (debounce #(onChange (-> % .-target .-value)))


@vmarcinko: I'll do a specific test soon on putting an ident on Root...I cannot think of why that would be a problem...just generates an extra edge in the graph, but I have never tried there is possibly something I'm missing.


Is there a way to passed om/computed data to the root? I'm writing a devcards using @tony.kay's untangled-app macro but the component I'm passing to untangled-app expects computed data.


offset by one...make root just be a non-ui concern that is about rendering your real root


In general, just treat Root as a special case that has a single top-level div for the react-key and locale...and push everything else down a level


yeah that's what i had in mind, just wanted to make sure there wasn't a cleaner way


makes sense, thanks!


@tony.kay: I have a use case in mind that I'm pretty stuck. How do we deal with different params being set on a component and on a class path. The use case is : I have a root and and active component which will be swapped later when I change route. When I change route I do initial set-query so the whole root would be reindex, at this point the query in set-query is the default one ( which is not the valid one to be sent to server ). Then when the new active component mounted, I set-query on this component ( now this makes thing complicated ), this is the right query and will be sent to server. The problem is, when the result come back from server, it get reread through nested query from root, and therefore we got the unvalid params in the read function. This is annoyed because I do check the params to determine that I should return the correct results or not ( prevent staled data ). How do you deal with this nested read, component swap in untangled ? I'm tempted to use untangled but I would like to understand om next well first. Thanks simple_smile


I don't recommend using set-query...pretty much at all at this point.


use unions. See the untangled-demo app on my awkay repo from my most recent shows switching in/out things....the cookbook also has an example, but I'm not done with it yet


I don't have time to read your entire question...sorry, very busy I realize that isn't a great answer 😉


Antonio on #C06DT2YSY can tell you more about the weaknesses and such of dynamic queries...Untangled simply does not support parameters on props or joins at if you're trying to do that, it won't work...I can talk about workarounds, but not today simple_smile


see my two recent talks...the one in PDX I go over this in detail (what to do instead of parameters)


nice, it's good to know. I have a really hard time working with parameters, it's just not right

tony.kay17:04:30 is just not right simple_smile That's what our opinion is too


nice idea...but D.N. is exploring ideas to prove them out...not all of them will work out


we're being pragmatic: what works well


yeah, will try out the union approach simple_smile


hey all, looking for some feedback for those who are interested (@currentoor, @cjmurphy, any others?). we are in the process of improving / completing the error handling story. the initial idea is to have a global error in the app state keyed at :untangled/server-error, which is set by the networking object’s send function when it receives an error. we would provide a hook point when creating a new untangled client to provide a mutation symbol that could be used to define (optionally) a global error handler. you could also provide a mutation symbol to the load-data functions to handle failed reads, and the currently implemented tx/fallback mutations would be used for mutations.


so the order of execution would be: 1) server sends an error to the client (status code, message, body) 2) client saves the error to the untangled/server-error key at the top level of app state 3) global error handler defined as a parameter to new-untangled-client is called 4) error handler specific to read or mutation that failed on the server is called


there would not be a default implementation of error fallbacks, so if the user doesn’t clear the error from the app state, it will stay there until another error overwrites it. so the user could have the option to query for the error anywhere in the UI using a link and determine what to show / what not to show based on that error


@ethangracer: so far we just added our own error event listener to the xhr-io object inside untangled client, that worked for us.


In the case of a failure, would both the global error handler be called and the mutation specified in tx/fallback?


@currentoor: we haven’t decided on that yet, that was one of the remaining big questions


I think it makes sense both ways for different reasons


the global error handler is meant to be global, so shouldn’t it be called every time? then again, fallback mutations could be meant to override it


really just a design choice


and, what makes sense for the people who are using it simple_smile


To me, a global error handler is only useful for a limited one or two cases, unauthorized or unauthenticated. Other errors IMO are mutation specific. So I think it's fine for global error handler to alway execute.


global error handler in our case just does stuff for 401 or 403, it would be nice if the error status is not either one of these then global handler warns us that there does not exist a tx/fallback (if one does not exist for this mutation)


thanks for asking!


Hey guys, good job with the untangled project. I'm following the project since the public release and can't wait to see more. @ethangracer the error handling sounds good for me. But I have to think more about the global error handler idea. I dunno if it's the right choice. @tony.kay the cookbook is a great idea. Here is the wish list. * Server-side security for UI-generated queries * Generate a new entity (UI/Server) * Paginate through a very large list of items * Push live data updates from the server with Sente


@currentoor: right, it’s nice to have something that catches certain kinds of errors from any mutation, AND errors that are triggered only by certain ones. so something like todo/new-item might return a 403 if the user isn’t authorized, and that would be handled by the global handler, whereas if the actual action of adding a new item failed, then the transaction specific callback would clean that up


and there’s no need to put the 403 logic in every fallback


i’m with you, thanks for the input!


@baris: let me know if you have more thoughts


going to be working on this for the next day or two


@ethangracer: could not have said it better myself


Is there a way to access the queue of remove mutations? If my first request failed, I want to undo my app-state and flush the subsequent mutations?


@baris: I added your list to the Wiki. Anyone else: Please vote there:


@tony.kay: thanks. Is it possible to extend ur recipes list? I'd like to have a full multi form CRUD example. With basic form fields like, textfield, textarea, date picker, checkbox, combobox, file upload. I'd be glad if I can help.


sure...the wiki is editable by anyone, so add it to the list there, and put your uname after it...hopefully others will vote. If you want to write it, even better. There is a script in the repo (new-recipe) that will create a starting template for you and add a bullet item to the README...just run it: ./new-recipe form-wizard "Wizard-like form filling" or something similar


then open the recipes/form-wizard as a project


and edit the README to move the bullet item into the right place


The generated template has client, server, root UI, etc.


Tomorrow, I'll have a look at it.


Not sure how to edit that wiki so I cloned it locally, but got this when I tried to push:

Failed with error: fatal: unable to access '': The requested URL returned error: 403


on the wiki itself...if you are signed into github you should see a "Edit" button (gray)


@cjmurphy: is it not there for you?


There is no edit button for me


missed the option


it should be there now


yes - my face is there in top right corner


try again...I missed the option to make it editable


got it now simple_smile


great, thanks for letting me know


@tony.kay: about the conversation we had before, you were right, I was able to use the global state for it, thank you simple_smile


welcome. Glad to hear it