There's also cljfx for desktop apps, fyi. It implements a lot of React concepts on top of JavaFX
@smith.adriane since the browser itself does focus and mutable inputs for you, React is a hack on top of that. So, to emulate it in a Fulcro-compatible way would mean you’d have to have a VDOM like thing, but then Fulcro is never really in control of focus…you cannot control that with React and state very easily at all.
so there won’t be an example of controlling a detail like focus
not to say you can’t do it, but it’s a lot of bookkeeping
And the js ecosystem has nice controls in React for form stuff…you could look at the F2 book…I think I had some samples in there…hold on
right, I was trying to see if there was anything analagous available in fulcro. for example, re-frame has re-com
it doesn't have to emulate a text input (which is unlikely since almost everyone except code mirror uses the built in input). anything that has state that is optionally controlled by the parent component would probably be good enough to get started.
> to emulate it in a Fulcro-compatible way would mean you’d have to have a VDOM like thing I'm trying to avoid that. There's nothing special about state that describes text selection, cursors, or focus that, in principle, couldn't be handled just like the other state fulcro manages.
So, this old F2 code (which uses out-of-date syntax) would not be hard to port. I did not bring them forward to 3. https://github.com/fulcrologic/fulcro/blob/2.8.13/src/main/fulcro/ui/bootstrap3.cljc#L526
not sure what you’re looking for exactly
I got out of the business of making these kinds of things a long time ago, so not much in my OSS libs has it
except for RAD semantic ui plugin, but it’s a bit complex for an example to follow
this is great! for example, it seems like the ::open?
attribute could be completely managed by the dropdown component, but it could also be partially controlled by the parent component
I was also curious how to handle defaults for a generic component, which this also does https://github.com/fulcrologic/fulcro/blob/2.8.13/src/main/fulcro/ui/bootstrap3.cljc#L524
yeah, it would be very nice to have a lib of official Fulcro components, but the js ecosystem has so many that can be controlled, it’s (nearly) pointless waste to write them (for my time)
well, there’s more to it. You’re going to end up wanting multi-root renderer
(probably)
otherwise you have to connect the state of components to the graph to initialize them (via mutations)
in an ideal world (which doesn't exist), a lot of the state management would be more reusable across platforms and environments. not sure how far I'll get, but I enjoy working on it 🙂
there’s an example here of what that looks like: https://github.com/fulcrologic/fulcro/blob/develop/src/workspaces/com/fulcrologic/fulcro/cards/multi_root_cards.cljs#L25
the idea is that you can generate an id on the fly, put it in state, render in “disconnected” from the normal data graph, and even GC it when you unmount
can't I just use default values if the data is missing (ie. uninitialized)?
remember that Fulcro is a View = F(state) library. The F in this case is a root pull of the database using the root query. If your data isn’t composed into that graph, you won’t get it
so, when you “spring” a component into existence you have to add it to that data graph. The initial set will already be there if you compose them via initial state, so yes, so for things that are truly a static and persistent part of your UI, you’re fine
When things get dynamic, you have to keep the graph in tact…so you might, for example, use merge-component!
to merge a new instance of a control into state, and then join it somewhere in the graph (as shown in that video)
But in forms you’ll find you’d rather not complect the control with the data…in which case having stand-alone controlled components is very very useful
:user/profession "Programmer"
doesn’t mix well with suddenly wanting to replace it with the graph of a UI control that hosts the data {:user/profession {:ui/open? true :options […] :value "Programmer"}}
the latter is a mess
so then you could make something parallel: {:user/profession "Programmer" :user.profession/control {:ui/open? true …}}
but then when you go to load some user (if you go there) you have to merge that control data with the raw data.
it’s why I mentioned floating roots. You’d rather have the controls data “spring to life” on demand, and go away when done.
I use semantic ui react for most UI, so I very very rarely actually write such controls.
but you’re getting into the business of writing that kind of this with what you’re doing, so…
very cool. I’m looking forward to trying to apply all these ideas. Thanks again for your help!
Good seeing you here, @smith.adriane — I love the work you did with membrane, and implementing re-frame over it via Skia: https://github.com/phronmophobic/membrane-re-frame-example. As well as your articles on revisiting UIs from first principles! https://blog.phronemophobic.com/what-is-a-user-interface.html
When trying to add fulcro's transit middleware to my ring server, I get:
java.lang.RuntimeException: java.lang.Exception: Not supported: class org.eclipse.jetty.server.HttpInputOverHTTP
WriterFactory.java:65 com.cognitect.transit.impl.WriterFactory$1.write
transit.clj:171 cognitect.transit/write
transit.clj:168 cognitect.transit/write
api_middleware.clj:151 com.fulcrologic.fulcro.server.api-middleware/write
api_middleware.clj:148 com.fulcrologic.fulcro.server.api-middleware/write
AFn.java:160 clojure.lang.AFn.applyToHelper
AFn.java:144 clojure.lang.AFn.applyTo
core.clj:667 clojure.core/apply
core.clj:6185 clojure.core/update-in[fn]
core.clj:6186 clojure.core/update-in
core.clj:6172 clojure.core/update-in
RestFn.java:494 clojure.lang.RestFn.invoke
api_middleware.clj:169 com.fulcrologic.fulcro.server.api-middleware/wrap-transit-response[fn]
...
Am I doing something wrong? Is this issue described in pathom docs related to this: https://blog.wsscode.com/pathom/#_fixing_transit_encoding_issues? I am not sure how to apply that fix that they suggest.Ah, stupid mistake, wasn't parsing the request. Now back to the problems with parsing transit in the request...
I am learning more about the rendering internals and this puzzles me from the keyframe 2 renderer :
(if limited-refresh?
(let [{limited-idents true} (group-by eql/ident? only-refresh)]
(doseq [i limited-idents]
(ior/render-components-with-ident! app i)))
(kr/render! app options))))
It looks like it processes only components based on idents, not props like advertised in the book. Or am I missing/misunderstanding something?@jatkin Thanks, I'll read more around that topic
@jatkin So, what I don't understand is this: the bit from the keyframe-2 renderer code I posted in my first post grabs only idents from the :only-refresh
option and discards anything else, including props, doesn't it?
Right? Not sure I follow the issue.
Keyframe renderer 2 has a different supported feature set. That is why the group-by
function is there... :only-refresh
can either take
1. Idents
2. Prop Keys
I think either KR1 or Ident based renderer support refreshing based off of prop keys. However KR2 only has the feature set to refresh by Idents.
@jatkin So then I got confused by this passage in the book which clearly demonstrates that KR2 can refresh by prop keys whereas as you agree, the source clearly states otherwise: https://book.fulcrologic.com/#_using_keyframe_render_2
oh, I'm a dumb dumb. It delagates everything to the original keyframe renderer, which does have support for prop keys
or... Hm. I've not dug this far into the rendering before. The ident renderer is the only AFAICT that supports prop keys refresh
Correct, ident optimized is the only one that bothers with prop refresh these days.
cool. Book is slightly out of touch then. I can submit a PR if it's helpful. I'll read a bit more before I try to make the book canonical again 🙂
Only components with idents are refreshable without a root re-render anyway…and if you’re using any of these later renderers they default to render from root, so there is no longer a need to refresh by prop (which just looked up all the component idents whose queries had that prop and refreshed the nearest parent with an ident).
The ident-optimized render used to be the default renderer…the book is probably just out of date with respect to that
@adam678 is there something in particular you’re trying to code, or just learning internals?
@tony.kay Learning internals and I was wondering how important it really was to use different kws just for optimization purposes. I don't really like exploding many keywords for the same thing (eg. making :person/name
and :dog/name
instead of just :<http://my.app/name
|my.app/name`>).
Without prop refresh I understand that I don't have to worry about that and can use :<http://my.app/name|my.app/name>
without being "un-idiomatic" but please correct me if I am forgetting about something else 🙂
It is a very good idea to namespace your keywords to their domain, and making a generic “http://my.app/name”, while sometimes appropriate (a much better example would be :entity/owner
, where you plan on pointing most entities at their owner), doing so as a “hygiene move” will cause you problems with other parts of your domain that have nothing to do with Fulcro. Remember that resolvers use these names for context, as can your code..having a mixed bag of overly generic stuff is like coding Jello…so don’t go overboard on making things generic: it will bite you.
As far as rendering optimizations went: prop refresh was a big hassle for people, led to a lot of confusion, and didn’t really actually help in real applications. The overhead if finding everything to refresh was often as bad as just rendering from root. So, I dropped the complexity.
Fair enough, thanks!
The if is the key I believe. You can supply it an option to ask for only a limited refresh I think. But it isn't the normal mode of operation.
@tony.kay @holyjak I meant this example from the book:
(defsc SomeForm [this props]
{:ident (fn [this props] [:component/id :form])
:query [:some/prop]}
(dom/input {:onChange (fn [evt] (comp/transact! this [(save ...)] {:only-refresh [:some/prop]}))}))
In a few words, what is supposed to ensure that components which query :some/prop
will possibly re-render? What I understand is that that those are filtered out and not processed.It's part of the indexing... Will link shortly. But, the limited-refresh?
is triggered by
https://github.com/fulcrologic/fulcro/blob/6841a3a6cf63a787f180a1234f967bf3cf610b3a/src/main/com/fulcrologic/fulcro/components.cljc#L910
https://github.com/fulcrologic/fulcro/blob/develop/src/main/com/fulcrologic/fulcro/rendering/keyframe_render2.cljc#L18
I am trying to modify the fulcro template, and want to store the auth token in local storage, but can't understand how to do that. I understand that I have to add a remote, but I need to somehow parse the request in that remote? I just want to query one key in the local storage basically. The second question is how to save it: in the template there is a session component, it stores the username and whether the session is valid. I want to change the session state machine to load the auth token first, and then query the backend for the session details using this token. Do I need to make a new AuthToken component with a new query [:auth-token], add it as another actor to the state machine, and use uism/load
with that actor?
I guess a more concise version of the second question is: if I want to query data from a remote, do I have to have a component whose query corresponds to that data that I want?
Sounds as something worth a PR, perhaps to ignore the target and log a warning in such a case?
Hello! I've published v0 of https://blog.jakubholy.net/2020/troubleshooting-fulcro/ Feedback & additions welcome. Source: https://github.com/holyjak/blog.jakubholy.net/blob/master/content/asc/posts/2020/troubleshooting-fulcro.asc
Freaking awesome. So helpful!!!
This is what I did to read the token from local storage. Does this seem ok? The fact that I need to do eql/ast->query
seems weird, isn't the query first specified as eql and then fulcro converts it to ast? How would I add mutations to this, so that I can write the auth token on login?