fulcro

Book: http://book.fulcrologic.com, Community Resources: https://fulcro-community.github.io/, RAD book at http://book.fulcrologic.com/RAD.html
Aleksander Rendtslev 2020-12-09T01:32:23.202100Z

I noticed that fulcro has barebones configuration for clj-kondo: https://github.com/clj-kondo/config/tree/master/resources/clj-kondo.exports/clj-kondo/fulcro Has anyone played around with writing the configuration for defmutation and some of the other fulcro specific macros? Otherwise I’ll be taking a stab at it in the next few weeks, but I just wanted to ask if someone has cracked this one already

Aleksander Rendtslev 2020-12-09T09:56:01.210400Z

Ha ha, the timing. Thank you for pointing this out @borkdude

tony.kay 2020-12-09T01:53:35.202200Z

Not aware of any.

2020-12-09T02:35:53.205300Z

I’m playing around with fulcro’s semantic-ui-wrapper and I’m having trouble sending a javascript “elementType” from the wrapper. For example, ui-sidebar has a prop :as which takes a string or elementType:

(sf/ui-sidebar 
{:as        "Menu"
:animation "overlay"                                                         
:icon      "labeled"                                                                      
:inverted  nil                                                                                          
:vertical  nil                                                                            
:visible   true                                                                            
:width     "thin"}                                                                          
(sf/ui-menu-item                                                                                
{:as "a"}                                                                          
(sf/ui-icon                                                                                           
{:name sfi/home-icon}) "Home"))

2020-12-09T02:39:22.207800Z

the :as is working fine for the ui-menu-item but not for the ui-sidebar . Any ideas what I should pass in props so that :as "Menu" appears as a an object on the javascript side?

Jakub Holý 2020-12-09T14:53:53.243400Z

I don't understand the problem. You are sending a string but it is ignored? I guess the component has a list of values it actually supports for the string? Or you mean that instead of a string, you want to send a JS object representing an element type? What is it, something from the DOM api?

2020-12-09T16:02:07.247300Z

Hey Jakub! 🙂 I’m sending a string from cljs (the api says string or function is acceptable). When I inspect the javascript using react tools the props appear as a string. However, if I inspect the semantic example code the props appear as an object. These images might help to clarify. The first one (light theme) is my non-working code. Notice that the Menu props is coming in as a string.

2020-12-09T16:04:43.247800Z

Here is the working example code. Notice that the Menu prop is coming in as an object.

Jakub Holý 2020-12-09T17:41:43.252900Z

I see. Can I see the working code that creates the dark screenshot?

2020-12-09T17:44:56.253400Z

Then use the react dev tools button at the bottom right to inspect the elements.

2020-12-09T17:46:02.253600Z

2020-12-09T17:51:40.254Z

I’m having problems getting the react tools to work on http://codesandbox.io if you’re seeing the same thing I started it from https://react.semantic-ui.com/modules/sidebar/#types-sidebar To get a new instance of codesandbox just click the CodeSandbox button on the first example.

2020-12-09T17:53:06.254200Z

i’m afk for about 10 mins…

Jakub Holý 2020-12-09T18:27:53.254400Z

I see, what do you send in your code? I assume you should send something like sui/menu (if they want the factory) or try directly the JS Menu class from raw sui

2020-12-09T18:28:24.254600Z

I’m sending a cljs string atm which is not working 🙂

2020-12-09T18:28:53.254800Z

I’ll try sending sui/menu… hold on

Jakub Holý 2020-12-09T18:29:40.255Z

(I'm on my phone so cannot check stuff easily so I'm hljust guessing names etc)

Jakub Holý 2020-12-09T18:31:06.255200Z

Question is what do they want, class, factory, or actual react element? Try all 3...

2020-12-09T18:33:25.255400Z

yah, the docs say “string” or “function”. Unfortunately my javascript interop skills are not great so I’m struggling to find the right invocation. I appreciate the help and please don’t interrupt what you’re doing - I’ll hack around a bit and let you know!

Jakub Holý 2020-12-09T19:45:41.255600Z

So, did any of that work?

2020-12-09T19:53:08.255800Z

getting closer but I haven’t solved it yet.. I know that I need to supply a class but I don’t know js well enough to tell cljs to pass a class directly to js…

2020-12-09T19:55:06.256200Z

The error is Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. that’s why it’s working for "a" in the example (it’s built-in so it expects a string). Menu is not built-in so it expects a javascript class/function

2020-12-09T19:56:00.256400Z

Unfortunately, I’ve got to run for an apt.. I’ll check back in a couple of hours!

Jakub Holý 2020-12-09T19:57:11.256600Z

What about (:require ["semantic-ui-react" as js-sui]) and passing in js-sui/Menu?

2020-12-09T23:31:46.269800Z

That did it! 🎉 The menus are still not rendering properly but that’s probably a different problem.

2020-12-10T01:25:51.271400Z

Working!

1
borkdude 2020-12-09T07:56:21.209900Z

There was just a discussion on this in clj-kondo. You’re welcome to contribute to the config. Libraries can now also export their config on the classpath. See #clj-kondo

Helins 2020-12-09T10:54:58.213600Z

Small thing: I often use a pattern where I unconditionally set-state! in :componentDidUpdate, relaying on :shouldComponentUpdate for breaking the endless loop. However during hot-reloading, everything is force-rendered in a way that do result in an endless loop and ultimately in an exception. Is there a more elegant solution than comparing myself the new state with the old one, which is cumbersome knowing that :sCU already does it 99,99% of the time?

avocade 2020-12-09T14:03:51.218400Z

Hey guys, having some weird issues with fulcro-inspect-electron, it won't load even when using the com.fulcrologic.fulcro.inspect.websocket-preload preload in shadow-cljs.edn… using the latest 3.0.2 of the app. cljs.core.ExceptionInfo {message: "No reader function for tag fulcro/tempid.", data: c…s.c…e.PersistentArrayMap, cause: null, name: "Error", description: undefined, …} The same exact code is working for my colleague, so there shouldn't be anything wrong with the tempid stuff (in the pathom parser).

Aleksander Rendtslev 2020-12-18T13:56:26.106700Z

@avocade did you figure this one out? My Fulcro Inspect worked yesterday, but after a reboot it starts throwing this message. The log coming out from my simulator indicates that it does connect

Aleksander Rendtslev 2020-12-18T14:07:39.106900Z

• Fulcro Inspect throws the error messages when I reload the app in the simulator • I see this message in the logs of the simulator: 2020-12-18T13:59:12.033Z DEBUG [com.fulcrologic.fulcro.inspect.inspect-client:192] - Devtools Message received {:type :fulcro.inspect.client/request-page-apps, :data {}} • I see this in the inspector tab of Fulcro Inspect

cljs.core.ExceptionInfo {message: "No reader function for tag fulcro/tempid.", data: c…s.c…e.PersistentArrayMap, cause: null, name: "Error", description: undefined, …}cause: nullcolumnNumber: undefineddata: cljs.core.PersistentArrayMap {meta: null, cnt: 2, arr: Array(4), __hash: null, cljs$lang$protocol_mask$partition0$: 16647951, …}description: undefinedfileName: undefinedlineNumber: undefinedmessage: "No reader function for tag fulcro/tempid."name: "Error"number: undefinedstack: "Error: No reader function for tag fulcro/tempid.↵    at new cljs.core.ExceptionInfo (file:///Applications/fulcro-inspect-electron.app/Contents/Resources/app.asar/js/renderer/main.js:3617:47)↵    at Function.cljs.core.ex_info.cljs$core$IFn$_invoke$arity$3 (file:///Applications/fulcro-inspect-electron.app/Contents/Resources/app.asar/js/renderer/main.js:3620:72)↵    at Function.cljs.core.ex_info.cljs$core$IFn$_invoke$arity$2 (file:///Applications/fulcro-inspect-electron.app/Contents/Resources/app.asar/js/renderer/main.js:3619:449)↵    at Function.cljs.tools.reader.impl.errors.throw_ex.cljs$core$IFn$_invoke$arity$variadic (file:///Applications/fulcro-inspect-electron.app/Contents/Resources/app.asar/js/renderer/main.js:8292:168)↵    at Function.cljs.tools.reader.impl.errors.reader_error.cljs$core$IFn$_invoke$arity$variadic (file:///Applications/fulcro-inspect-electron.app/Contents/Resources/app.asar/js/renderer/main.js:8294:141)↵    at Object.cljs.tools.reader.impl.errors.throw_unknown_reader_tag (file:///Applications/fulcro-inspect-electron.app/Contents/Resources/app.asar/js/renderer/main.js:8330:120)↵    at cljs.tools.reader.edn.read_tagged (file:///Applications/fulcro-inspect-electron.app/Contents/Resources/app.asar/js/renderer/main.js:8524:117)↵    at cljs.tools.reader.edn.read_dispatch (file:///Applications/fulcro-inspect-electron.app/Contents/Resources/app.asar/js/renderer/main.js:8483:106)↵    at Object.cljs.tools.reader.edn.read_delimited (file:///Applications/fulcro-inspect-electron.app/Contents/Resources/app.asar/js/renderer/main.js:8494:418)↵    at cljs.tools.reader.edn.read_vector (file:///Applications/fulcro-inspect-electron.app/Contents/Resources/app.asar/js/renderer/main.js:8496:317)"__proto__: Error
Anything you’ve seen before @tony.kay?

Aleksander Rendtslev 2020-12-18T14:07:51.107100Z

(I posted this on Discussions as well, so it’s captured outside of slack)

Aleksander Rendtslev 2020-12-18T15:06:24.107300Z

Shoutout to @wilkerlucio for answering and capturing this: https://github.com/fulcrologic/fulcro/discussions/441

🎉 1
Henry 2020-12-09T14:48:58.243100Z

Just finished reading the developer's guide for Fulcro RAD and have a question: how does one go about pushing a Fulcro RAD app into "production"? Totally understand that it is still in alpha and not suitable for "real stuff", but getting a sense of how the real endgame could play out completes Fulcro's full-stack story. The answer is probably DIY... However, having an approachable deployment story would make Fulcro even more appealing. Does anyone has any experience/best-practice/tutorial/blog/video to share? Thanks in advance for sharing.

Henry 2020-12-10T14:04:41.274700Z

Thank you for sharing.

Jakub Holý 2020-12-09T14:51:42.243200Z

We run it in prod. I build an uberjar including the shadow-built frontend code

tony.kay 2020-12-09T15:40:24.243600Z

Same.

tony.kay 2020-12-09T15:41:54.243800Z

What are you actually trying to do?

tony.kay 2020-12-09T15:43:52.244Z

Perhaps you are not running the version you think you're running? Installed in more than one place e.g.

tvaughan 2020-12-09T17:14:36.251600Z

I have a list of components, e.g. ul and li's. On each li I have an onClick event. Depending on the state of the component, I add a button with an onClick handler to a div embedded in the li. It seems as though when I click on the button both onClick events are triggered. I want to only trigger the button onClick event. I've set the z-index of the button so it's on top. Is my solution to this in Fulcro or CSS? Thanks

tvaughan 2020-12-09T17:21:45.251700Z

Answering my own question, I just don't add the onClick hander to the li when the button is also added

2020-12-09T17:23:33.251900Z

Sounds like the event is bubbling up. You could also call (.stopPropagation evt) in your inner handler if you don't want the parent one to be called.

Björn Ebbinghaus 2020-12-09T17:25:54.252300Z

There is even the com.fulcrologic.fulcro.dom.events namespace with a cljc (and Cursive) friendly (stop-propagation! evt)

Björn Ebbinghaus 2020-12-09T17:26:13.252500Z

And the even more used prevent-default!

tvaughan 2020-12-09T17:35:47.252700Z

Awesome. Thanks! This is much better

Thomas Moerman 2020-12-09T19:53:14.256Z

Cool, didn't know fulcro had these util fns. I should read the entire API some day instead of reinventing the wheel all the time 😅

genekim 2020-12-09T20:31:55.258500Z

Thanks for all the help so far! I’m delighted beyond words that yesterday, I got my first RAD report generated, so that SessionReport displays the contents of :session/all-sessions retrieved from Datomic Cloud — that was amazing! (Screenshot attached.) I then made a SessionForm, but it’s always showing an empty form — I’m pretty sure it’s because I didn’t write the resolver correctly, as it doesn’t appear to get called.

#?(:clj
    (pc/defresolver session-by-eid [{:keys [db] :as env} input]
      {::pc/input #{:db/id}
       ::pc/output [:session/title
                    :session/venue :session/start-time-utc :session/speakers :session/sched-id]}
      (queries/get-session-from-eid db input)))
The get-all-sessions does work, and it returns something of this shape:
#:session{:all-sessions ({:session/conf-sched-id "5348024557502565-32",
                          :session/conf-id #:db{:id 5348024557502565},
                          :session/venue "All Tracks",
                          :session/title "Opening Remarks",
                          :session/sched-id 32,
                          :session/start-time-utc #inst"2020-10-13T15:30:00.000-00:00",
                          :session/type #:db{:id 9649314045362249, :ident :session-type/plenary},
                          :db/id 1561306511442102,
                          :session/speakers "Tony Kay, Jakub Holy, :)"}
                          {}{} ...)}
Any chance anyone can spot what I’m doing wrong? Thx again!

tony.kay 2020-12-10T16:20:49.275800Z

No. Attr are used to define the model, and if you want the back-end plugins to work you need to have an id for each entity that is namespaced to that entity. Something like :session/id should be marked ao/dentity? true and the attrs that live with it should point via ao/identities . Report resolvers can be a ref resolver if you want, but their attr will be some invented name like :session/all-sessions . If you want the Datomic plugin to autogenerate resolvers for your session you need only generate the proper attr setup for the entity itself, and register the attribute in all-attributes. Then a resolver like :session/all-sessions need only return {:session/all-sessions [{:session/id id} …]} and pathom will glue together the UI-required data.

tony.kay 2020-12-10T16:23:42.276Z

The book discusses most of this, and the demo gives an example of it all. I understand that playing with it helps with understanding, so I’m not sure where the disconnect is. To understand RAD you need to make sure you understand how Pathom works. All RAD is doing is generating queries that pathom will resolve. The plugins have some autogenerators, but you do not have to use them. It’s just a graph API, and the attrs are a nice way of centralizing the description of entities. Top-level queries are hand-written, but automatically get the benefits of any entity resolvers (thus why session all sessions resolve can just return IDs)

tony.kay 2020-12-10T16:25:06.276200Z

Form save is similar..the mutation just has to take a diff and be able to apply it. The plugins have a prewritten alg for that, but there’s not a ton to it.

tony.kay 2020-12-10T16:29:01.276400Z

So, problems in your current code: 1. Do not use :db/id as an attr. Ever. The support for native IDs can map (lightly tested) attr ids to native IDs, but RAD expects the namespace of the id attribute to be unique per entity. See the demo. 2. ID attrs must be marked as ao/identity? true. See demo 3. Members of an entity must list that entity’s ID attribute keyword in ao/identities set, which MUST be a unique per-entity name. See 1. 4. Read-only data can be placed on a “virtual” attribute (with pc/resolve) but that only works if your db adapter knows how to autogenerate resolvers (Datomic does). Otherwise you write them as resolvers. The former is nice for integrating with reports since you can add addl data on them, but the latter is perfectly fine.

tony.kay 2020-12-10T16:30:39.276600Z

So, on (1) you can say (defattr id :session/id :long {do/native-id? true …}) (which maps :session/id to :db/id during processing) but I’d highly recommend (defattr id :session/id :uuid …) instead and let :db/id be sorta like postgresql OIDs.

tony.kay 2020-12-10T16:31:59.276800Z

The general pattern for anything you put in defattr will generally be (defattr nm :current-namespace/nm …). This is not enforced or required (or even always true), but it is an ideal rule for beginners.

tony.kay 2020-12-10T16:32:49.277Z

where current-namespace is usually the unqualified name (no package)…but adding package doesn’t hurt if you plan to do larger federated data later.

tony.kay 2020-12-10T16:39:03.277300Z

On other thing on native IDs: I understand the desire to use them, and once you get the hang of it all and are ready to contribute 😉, then try out native ID support and send bug fixes. I don’t use that feature myself, and don’t know what bugs may be there. I vastly prefer to have a UUID on each entity.

genekim 2020-12-10T17:46:16.282700Z

That's super, @tony.kay — thanks for the explanations. I'll get the UUIDs into the session schema, to get my example to mirror the demo examples. It was magical seeing the SessionList get materialized, so I'm looking forward to the same reaction when I get SessionForm running. 🤞🤞 I'm keeping pretty detailed daily notes of my experiences trying to get this running, which I'll write up for you and team. If all goes well, I'll have a couple hours to work on it today/tonight, as well as tomorrow. Hopefully you'll see a triumphant report that I got it working by then. 😂

genekim 2020-12-09T20:34:24.259Z

Googling around, I was initially suspicious about whether I could pass in an Datomic entity-id into a resolver…. I found this in the Clojurians Slack, that seemed to indicate that you can. I’m wondering if there’s a way to more directly call this resolver…. I’ve been trying something like this from the REPL — I thought it worked before…

(com.example.components.parser/parser com.example.components.config/config
                                          [{[:db/id 70368744177664139]
                                            [:session/speakers]}])

genekim 2020-12-09T21:01:41.259200Z

Oops: here was the question about using entity-ids in resolvers: https://clojurians-log.clojureverse.org/pathom/2018-12-28

genekim 2020-12-09T21:20:33.259400Z

…and here’s my forked version of RAD demo, working in branch: thx! https://github.com/realgenekim/fulcro-rad-demo/blob/gene-experiments/src/shared/com/example/model/session.cljc#L113

Björn Ebbinghaus 2020-12-09T21:43:16.267500Z

Does anyone have a tip on working with nested dynamic routers with parameters? for a path /foo/&lt;param1&gt;/bar/&lt;param2&gt; Should I nest two routers one for`/foo/<param>` and one for /bar/&lt;param2&gt;? Intuitively I would say yes. But then there is the question on how to pass the context from the first router to the targets of the second one. Otherwise maybe it would be better to have just a single router? and have ["foo" :param1 "bar" :param2]as the route-segment?

genekim 2020-12-09T21:55:35.268200Z

Oops. Annotated the 2nd screenshot here.

Jakub Holý 2020-12-09T22:26:46.268800Z

I pass it as a computed prop but I guess the 2nd solution is worth trying, if supported at all

Jakub Holý 2020-12-09T22:30:59.269Z

I believe you can pass any number of extra params to route-to or what is it called. RAD reports use it a lot. So you could modify the html5 history integration to trigger a route with these params, (route-to.. {:param1 v1, :param2 v2). Just guessing here... (RAD stores all params inside a base64 encoded EDN map in a query params, not the path, but the principle would be the same) From the book: (comp/transact! this [(r/route-to {:handler :detail :route-params {:kind kind :id id}})]))]

Björn Ebbinghaus 2020-12-09T22:36:48.269400Z

But that is legacy router stuff.

Björn Ebbinghaus 2020-12-09T22:48:04.269600Z

The second solution is superior in that it solves the context problem and it allows unique routing for a :param1 :param2 pair. The nested router solution is just easy for scoped UI that is common to all targets. Actually the nested router way could be problematic. Since the identity of the inner router isn’t depending on the first parameter. So the inner router would have the same state for /foo/VALUE1/bar/<param2> and /foo/<VALUE2/bar/<param2>