Fork me on GitHub
#fulcro
<
2020-12-09
>
Aleksander Rendtslev01:12:23

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

tony.kay01:12:35

Not aware of any.

borkdude07:12:21

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

Aleksander Rendtslev09:12:01

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

alex-eberts02:12:53

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"))

alex-eberts02:12:22

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ý (HolyJak)14:12:53

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?

alex-eberts16:12:07

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.

alex-eberts16:12:43

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

Jakub Holý (HolyJak)17:12:43

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

alex-eberts17:12:56

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

alex-eberts17:12:40

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.

alex-eberts17:12:06

i’m afk for about 10 mins…

Jakub Holý (HolyJak)18:12:53

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

alex-eberts18:12:24

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

alex-eberts18:12:53

I’ll try sending sui/menu… hold on

Jakub Holý (HolyJak)18:12:40

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

Jakub Holý (HolyJak)18:12:06

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

alex-eberts18:12:25

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ý (HolyJak)19:12:41

So, did any of that work?

alex-eberts19:12:08

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…

alex-eberts19:12:06

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

alex-eberts19:12:00

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

Jakub Holý (HolyJak)19:12:11

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

alex-eberts23:12:46

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

Adam Helins10:12:58

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?

tony.kay15:12:54

What are you actually trying to do?

avocade14:12:51

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).

tony.kay15:12:52

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

Aleksander Rendtslev13:12:26

@U06KD64RX 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 Rendtslev14:12:39

• 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 @U0CKQ19AQ?

Aleksander Rendtslev14:12:51

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

Henry14:12:58

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.

Jakub Holý (HolyJak)14:12:42

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

Henry14:12:41

Thank you for sharing.

tvaughan17:12:36

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

tvaughan17:12:45

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

Chris O’Donnell17:12:33

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 Ebbinghaus17:12:54

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

Björn Ebbinghaus17:12:13

And the even more used prevent-default!

tvaughan17:12:47

Awesome. Thanks! This is much better

Thomas Moerman19:12:14

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 😅

genekim20:12:55

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!

genekim20:12:24

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]}])

genekim21:12:41

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

genekim21:12:35

Oops. Annotated the 2nd screenshot here.

tony.kay01:12:38

In general I would discourage using :db/id. You end up with a ton of resolvers all saying “I can resolve that given a :db/id”, which gives no specificity to your system. I would highly encourage you to use things like :session/id as UUIDs. This also makes you database easily shardable later and make data migration easier. Technically to-many output should be in vectors, not lists or seqs. Pathom may heal those, but the formal expected data is vectors. https://github.com/realgenekim/fulcro-rad-demo/blob/gene-experiments/src/datomic/com/example/components/database_queries.clj#L119

tony.kay01:12:55

Your actual query function f a session returns nil:

tony.kay01:12:18

perhaps you meant (log/spy :info session)?

genekim04:12:02

Holy smokes. What a face palm moment! Thanks for catching that, @U0CKQ19AQ. I fixed the two return value issues you pointed out in the db queries — specifically, the vector shape of get-all-sessions and the stupid nil in get-session-by-eid. But for life of me, I can’t seem to get get-session-by-eid to be called by the resolvers. If I understand correctly, pathom looks at elements of the desired :pc/output, and searches for a set of resolvers to get there… So, somehow, I must be calling parser with the wrong input (`db/id`), or the wrong desired output (`session/title`, etc…) (Sorry, just thinking aloud here…. Checking my changes in, and going to stare at the screen some more! 🙂

genekim04:12:57

…my goal is to get the resolver working via entity-id first, and then get those uuid properties created…

tony.kay04:12:39

@U6VPZS1EK did you add it to the list of resolvers?

tony.kay04:12:28

guessing…not 🙂

tony.kay04:12:50

the resolvers are installed manually. You can make a macro and atom soln if you want, but by default it is a manual install on server.

tony.kay04:12:59

see all-attributes and all-resolvers

tony.kay04:12:04

which compose them all from other nses

genekim04:12:32

Oh! Okay, looking at how to get it fixed…. Right now, session-by-eid is a pc/resolver , which only shows up in the CLJ portions… Do I convert it to a defattr? (Because I’m guessing the CLJS client needs to see it?) Thanks for your patience and help, @U0CKQ19AQ!

genekim04:12:28

(You might see from all my commented out code how many permutations I’ve tried… I already see that I made a defattr version of session-by-eid… Trying that now. 🙂

tony.kay16:12:49

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.kay16:12:42

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.kay16:12:06

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.kay16:12:01

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.kay16:12:39

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.kay16:12:59

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.kay16:12:49

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.kay16:12:03

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.

genekim17:12:16

That's super, @U0CKQ19AQ — 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. 😂

Björn Ebbinghaus21:12:16

Does anyone have a tip on working with nested dynamic routers with parameters? for a path /foo/<param1>/bar/<param2> Should I nest two routers one for`/foo/<param>` and one for /bar/<param2>? 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?

Jakub Holý (HolyJak)22:12:46

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

Jakub Holý (HolyJak)22:12:59

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 Ebbinghaus22:12:48

But that is legacy router stuff.

Björn Ebbinghaus22:12:04

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>

tony.kay01:12:40

The route params that @U0522TWDA mentioned is how I’d do it. That is not legacy routing. The parameters passed to the routing instruction are made available to will-enter in all targets. They need not be part of the “path”. The path parameters make them part of the decision logic for routing…passing other data just makes them part of the params.