fulcro

Book: http://book.fulcrologic.com, Community Resources: https://fulcro-community.github.io/, RAD book at http://book.fulcrologic.com/RAD.html
tony.kay 2021-02-23T05:09:56.051100Z

:req nil? ?

markaddleman 2021-02-23T17:43:07.060600Z

Whoops. I have correct the example and the result is the same.

markaddleman 2021-02-23T17:44:37.061Z

The second explain results. It is as if guardrails is generating some data as a kv-pair when it should be generating it as a map (of course, this is only a guess)

Jakub Holý 2021-02-23T06:03:50.051400Z

No. Make latest when you insert the data in your client DB. How else could it be inserted to a unique place?

tony.kay 2021-02-23T06:50:14.051900Z

Part 5 of G.F. is uploading now.

🎉 6
tony.kay 2021-02-23T06:50:30.052200Z

Looks like 30 mins…

👏 5
Henry 2021-02-23T10:09:55.052400Z

The videos are simply awesome! Thanks so much Tony for taking the time to teach us and help us to really grok Fulcro. P.S. it seems Part 5 is not yet added to the G.F. playlist.

tony.kay 2021-02-23T15:42:56.059100Z

ooops

Timofey Sitnikov 2021-02-23T12:19:45.057200Z

OK, this is probably super obvious to the point that I cannot figure it out. Looking at https://github.com/fulcrologic/fulcro-rad-demo/blob/develop/src/shared/com/example/model/authorization.clj, there is a function that has an env as a parameter. If I wanted to run the login! function in REPL, assuming the Fulcro RAD demo server has been started, what would I use for env parameter?

tony.kay 2021-02-23T15:51:22.059500Z

The env is built by the plugins of the pathom parser. So, typically you’d write a login mutation, which would then be auto-passed an env when invoked through pathom. Otherwise, env is just a map, and you largely define what is in it. If the function in question doesn’t use much (or anything) from the env, then just make a map that has what it needs.

1
tony.kay 2021-02-23T15:52:24.059700Z

In this case, it expects env to have the Ring request which it expects has a session {:ring/request {:session {…}}.

tony.kay 2021-02-23T15:53:13.059900Z

(`get-login-info`, I think, uses the database connections out of env as well)

Jakub Holý 2021-02-23T17:40:56.060200Z

Under https://blog.jakubholy.net/2020/troubleshooting-fulcro/#_backend_pathom look at 1.d

Jakub Holý 2021-02-23T19:55:42.063Z

https://github.com/holyjak/fulcro-troubleshooting has been updated to check also idents are correct and that you are not missing data for joins (ie. your children) - typically because you loaded data into the wrong place or forgot initial state for a comp. that has only link queries. Please see the docs and give it a try! 🙏

👍 1
Timofey Sitnikov 2021-02-23T20:23:08.063200Z

Ok, this does shed the light on things. Probably quick and easy prototyping is to compose a map and feed it to the fn.

Aleksander Rendtslev 2021-02-23T21:51:53.067800Z

Side effects: I’m following the Login example from the fulcro template and so far everything makes sense. I’m still getting used to a lot of the framework indirection: eg. calling uism/trigger-remote-mutation understanding m/returning will send data to the component you specify and you’ll handle that in pre-merge is a little gnarly for me. Anyways, I’m using a token based flow, and I’d like to save the token to local storage so I can load it (or the refresh token), when the app starts up again. What would be the idiomatic way to achieve that? My current solution is to sneak in a (token-store/save-token token) call in the pre-merge call of the m/returning component, but I’m not sure about the lifecycle and implications of this

(defsc Auth
  "Auth representation. Used primarily for server queries. 
   On-screen representation happens in Login component."
  [_ _]
  {;; The fields we're querying in our local database
   :query         [:user/id :token]
   :ident         (fn [] [:component/id :auth])
   :pre-merge     (fn [{:keys [data-tree]}]
                    ;; Save the token if provided
                    (when (some? (:token data-tree))
                      (token-store/save-token (:token data-tree)))
                    (merge {:user/id ""}
                      data-tree))
   ;; Default initial state
   :initial-state {}})

Aleksander Rendtslev 2021-02-24T11:15:15.077700Z

Okay, I'll go down that path. Aliasing was definitely a cleaner solution for the other problem I was working on. Thank you! It's all coming together. A quick sidenote: Asynchronicity in state machines: On initialization I load the token from local storage, which is an asynchronous action. In order to make this work, I use the application atom to trigger the complete event once the token is loaded. It works fine. The difference is that I do uism/trigger! @app :event/complete As opposed to having a returnable uism/async function where you can provide it with a success and error event and have the handler return this normally. I'm guessing the two solutions would be what I did, or abstracting local storage out into a remote and use trigger-remote-mutation instead?

tony.kay 2021-02-24T16:42:18.077900Z

no, that’s not what you want. You’re triggering a remote mutation or load aren’t you? Those functions have options for triggering the event. The atom itself is useful for network middleware to avoid cycles…I would not use it anywhere else in this problem.

Aleksander Rendtslev 2021-02-24T16:45:05.078100Z

So the question is: Is there a way to do async actions in a state machine outside of using a remote? It is on my TODO to figure out how to wrap local storage in a remote interface, I just haven’t prioritized it so far

Aleksander Rendtslev 2021-02-24T16:45:20.078300Z

(thank you for clarifying all this by the way!)

tony.kay 2021-02-24T16:45:31.078500Z

ah, yes, localstorage would normally be a remote in the Fulcro model

tony.kay 2021-02-24T16:46:19.078700Z

so you mean you want an event after localstorage completes….It is perfectly fine to do (async-call-that-takes-callback (fn [] (uism/trigger APP …)))

tony.kay 2021-02-24T16:46:33.078900Z

I thought you meant you were adding a watch to the atom or something…I just misunderstood what you were saying

tony.kay 2021-02-24T16:49:34.079200Z

I basically consider the internals of a UISM event handler to be at a lower abstraction level. Moving I/O side-effects to a remote is more “purely clean”, but is a level of code hygiene that is sometimes overkill with such a localized concern IMO. For this use-case, I’d probably do what you did.

Aleksander Rendtslev 2021-02-24T16:52:52.079400Z

Perfect. That’s exactly what I figured. So far, all I’m writing is the refresh-token. It felt overkill to create an entire remote for that. So that’s exactly what I did. Load the token and use the async callback to trigger an event. Thank you for validating!

👍 1
Jakub Holý 2021-02-23T22:34:51.067900Z

Just to clarify : m/returning will not "send data to the component you specify". It tells Fulcro that the mutation returns a data and what data that is (described by the given component, because, remember, components describe your data model) so that it knows where to put it (based on the ident). Normally I would do storing of the token in ok-action of a mutation or in another mutation this triggers. But I haven't looked at your example so I might be off the mark here. Pre-merge certainly feels as the wrong place for side-effects. Those belong in mutations.

Aleksander Rendtslev 2021-02-23T23:16:27.068300Z

Thank you for the pointer. Digging for the event-data right now. Another thing: Is there a way to load app state side band? I’m asking because I need to add the token to my api request. Right now i’m loading it from an atom, but I’m thinking it might make more sense to load it from the client db where it’s saved anyways:

(defn wrap-with-token [req]
;; Could I load the token from the Auth 
;; component instead?
  (if-let [token @token-store]
    (assoc-in req [:headers "Authorization"] token)
    req))

tony.kay 2021-02-23T23:47:16.068500Z

I personally do not use pre-merge a lot, and that particular use of pre-merge is not great. I’d prefer that side-effect be in the UISM. Here’s what I’d probably do (in a UISM): On UISM start (which is app start, I’m guessing), I’d pull the token from localstorage (if present) and put it in app state. I’d use an alias for the Auth’s field that represents that token, so it is just an assoc-alias call. On the state that tries to log in you can tell it what events to fire as a result of a mutation. So, you’d trigger the remote mutation to do the login, with a returning of Auth…but you don’t need pre-merge because you already have the alias to where the token is in state, it’s just the aliased value. So, the event handler in UISM can just look to see if it got a valid token. Some other alias might track where you store your login error, etc.

tony.kay 2021-02-23T23:48:47.068700Z

on your second question: your middleware wrapper could use the app to pull state:

(defn wrap-token [app] (fn [req] ...))

tony.kay 2021-02-23T23:49:31.068900Z

That typically creates a circ ref, so this is where an app atom could be handy to break the cycle (app uses remote uses middleware uses app)

Aleksander Rendtslev 2021-02-23T23:58:46.069300Z

ohhh… Interesting… I’ll try that out @tony.kay. The aliased way. For pulling out of env I created this helper:

(defn env->mutation-result
  [env mutation]
  (get-in env [::uism/event-data ::uism/mutation-result :body mutation]))

tony.kay 2021-02-23T23:59:13.069500Z

yeah, don’t do that….it’s going into state already

Aleksander Rendtslev 2021-02-23T23:59:22.069700Z

I do have an app atom. Which function would allow me to pull it from the app atom? (The good thing about the book is that it contains almost everything I need. The bad thing is finding out where it is can be a little tricky)