fulcro

Book: http://book.fulcrologic.com, Community Resources: https://fulcro-community.github.io/, RAD book at http://book.fulcrologic.com/RAD.html
tony.kay 2020-12-18T00:03:01.097900Z

oh. funny. didn’t remember that

tony.kay 2020-12-18T00:03:07.098100Z

no one has tried this yet

tony.kay 2020-12-18T00:03:08.098300Z

😄

😁 1
phronmophobic 2020-12-18T01:12:39.098600Z

it works!

🎉 2
tony.kay 2020-12-18T01:27:21.099Z

😄

tony.kay 2020-12-18T01:27:33.099200Z

see, not hard at all

tony.kay 2020-12-18T01:28:07.099400Z

what UI is that using, just Swing?

wilkerlucio 2020-12-18T01:41:53.099700Z

maybe JavaFX?

phronmophobic 2020-12-18T01:42:59.100Z

skia https://skia.org/

phronmophobic 2020-12-18T01:43:29.100200Z

but it can render to a number of different targets, https://github.com/phronmophobic/membrane

phronmophobic 2020-12-18T01:44:22.100500Z

right now, there's skia, swing, and terminal on desktop, but I plan on supporting javaFX at some point

tony.kay 2020-12-18T01:44:58.100700Z

nice

tony.kay 2020-12-18T01:45:15.101Z

that is a really cool thing to combine with Fulcro

phronmophobic 2020-12-18T01:45:30.101300Z

I agree

phronmophobic 2020-12-18T01:45:54.101500Z

I had just about given up on integrating existing ui frameworks because, so far, they've been coupled to react

phronmophobic 2020-12-18T01:47:28.101800Z

Fuclro seem great. I'm kicking myself for not trying it earlier!

JAtkins 2020-12-18T04:47:17.102300Z

@ Adrian Smith Hey, if you get anywhere at all with skia lmk! I'd love to try and dogfood it with you, since I have no interest in writing more and more javafx/swing stuff and I have some projects I want to build that cannot be web based for performance reasons.

phronmophobic 2020-12-18T04:54:28.102600Z

😆 , poor other "Adrian Smith" that gets mentioned for random discussions I'm in.

phronmophobic 2020-12-18T04:55:35.102800Z

@jatkin, will do! I'm working on creating an example repo for a proof of concept

JAtkins 2020-12-18T05:03:12.103Z

lol

JAtkins 2020-12-18T05:04:02.103200Z

I actually tried to get the right one... slack didn't help me out. I was hoping the first in the list would be the one which was in the same thread. Why does slack not sort by relevance?

🤷 1
phronmophobic 2020-12-18T05:16:27.103600Z

it's not the first time this has happened 😁

Jakub Holý 2020-12-18T11:22:26.105200Z

In RAD, why is https://github.com/fulcrologic/semantic-ui-wrapper a :dev dependency? I would expect it to be a standard dependency, so I am obviously missing something important...?

2020-12-18T12:33:58.106400Z

It should only be used by cljs, and so it should only needed at build time.

👍 1
🤯 1
genekim 2020-12-18T18:59:22.120Z

Posting a brief update — I had the privilege of having my third pairing session with @holyjak yesterday using Fulcro RAD, and quite frankly, I’m in awe of what we got done in under an hour (after updating dependencies, getting Fulcro Inspector working, etc): I learned how to build a column-formatter for URLs, customer formatter for an enumerated type, and got first mutation working to allow server-side calls. What particularly stuns me is how mutations work: and how much of the work of getting data from the CLJS client and CLJ backend, and back again, is all just taken care of for you — on the client, there’s no need for cljs-http or async state management, marshaling of data on client and server, writing argument processing in ring/compojure.. To see our first mutation running round-trip was… awe inspiring, jaw dropping…. I put this experience up there with the top jaw-dropping moments I’ve had in my career: first Heroku push, first time using Ruby on Rails… It’s been a very challenging learning curve, and frankly, I’m in awe of how people are learning this without a significant amount of hands-on help from someone like Jakub. But, without doubt, there’s an incredible power and leverage that Fulcro RAD affords, which was obviously something I was hoping for, given the 40+ hours of time I’ve put into this so far And I know I’ve just barely scratched the surface of understanding how to do anything. Amazing work, @tony.kay! (Okay, back to trying to build a subform to associate Tags with Sessions! 🙂

🎉 5
genekim 2020-12-18T19:01:20.121700Z

@tony.kay I’m working off of the datomic-cloud branch of fulcro-rad-demo. I’m merging in your updates from main — if you’re interested, I can put together a PR with those changes merged in… Let me know…

tony.kay 2020-12-18T19:01:55.122700Z

sure, I just don’t have time to test stuff, so please make sure it is solid and the instructions are good, or else it spawns questions 🙂

tony.kay 2020-12-18T19:02:15.123200Z

and glad you like it all…RAD is a bit unpolished and underdocumented, but I only have so much time

genekim 2020-12-18T19:03:56.124600Z

Also, working off of main, I’m getting these warnings/errors in the console — are these problems? (The line-items reports no longer work in datomic-cloud branch, so I’m trying to get those working again…). Specifically I’m wondering about the UUID formatter — would that be easy to fix?

tony.kay 2020-12-18T19:04:45.125300Z

The error is from putting a UUID on a form. the SUI plugin does not have an input renderer for that data type

genekim 2020-12-18T19:26:43.126500Z

Is the problem just aesthetic, or does it actually prevent any demo functionality?

tony.kay 2020-12-18T19:33:47.126700Z

no idea 🙂 Been a while since I looked

tony.kay 2020-12-18T19:34:01.126900Z

I wasn’t aware I had used a UUID in a form field

felipethome 2020-12-18T20:22:15.131900Z

Hi! When reading about computed props in the book (F2) it says we can’t put code in the database, is there a section in the documentation explaining that better? Because no errors are raised when you insert a function in the DB so I would like to understand what are the consequences if someone does put code in it

Jakub Holý 2020-12-18T20:25:27.132200Z

At least Fulcro Inspect will suffer as DB needs to be serialized to send to it I believe.

Jakub Holý 2020-12-18T20:29:34.132400Z

Not form but a report I think. The invoice/id in the AccountInvoices report. I guess sending a PR to RAD adding a default formatter for uuid would be best

felipethome 2020-12-18T20:51:58.132800Z

I’m not sure but I think it will show as an unknown transit type

Gleb Posobin 2020-12-18T20:52:11.133200Z

Hi, I am just getting started with fulcro and trying to adapt fulcro-template to what I need. I want to pass an auth token to the server in a header on every request (if the user is logged in), but I am having trouble understanding how to make the fulcro-http-remote read the auth token from the db, and attach to every request.

felipethome 2020-12-18T20:57:50.133600Z

Maybe it is related to the diff

Tyler Nisonoff 2020-12-18T21:07:58.138400Z

I have a fair amount of db-persisted RAD attributes that I want to display defaults for on the frontend. I’m trying to a void a bunch of (or attr default-val) throughout the UI code. I’m currently experimenting with an approach where, say I have a :person/age field that I want to default to “10”. I’ll create a db-persisted attribute, :person/age* , and a virtual attribute, :person/age that reads :person/age* from the db, checks if a stored value exists, and returns 10 otherwise. Then on the frontend i always use :person/age in forms / views. And to ensure we can write new values, I rename any occurrences of :person/age -> :person/age* in my save middleware. I’m curious if others run into this pattern, or see any obvious pitfalls with this approach?

Jakub Holý 2020-12-18T21:10:16.138500Z

Hi, welcome! Nowadays it is perhaps better to start with RAD, which is Fulcro plus an extra library that you don't need to use. The demo is perhaps better maintained than fulcro-template. I am guessing here but perhaps you could leverage the :request-middleware option of the remote? And you have your global app which you can use to get at the DB and thus the token. Or you can just store the token in an atom...

Tyler Nisonoff 2020-12-18T21:10:44.138700Z

And i’ll add that for my use-cases, I wouldn’t want to necessarily just write the default value to the db the first time the entity is created. I have certain time-based properties that, if unspecified, I want the default to dynamically change over time.

Gleb Posobin 2020-12-18T21:10:45.138900Z

Hmm, weird, I looked at rad template and it seemed much less fully-featured.

Gleb Posobin 2020-12-18T21:11:30.139100Z

Ah, so I can access the app db externally.

Jakub Holý 2020-12-18T21:11:53.139300Z

Yes. And look at wrap-csrf-token in the ...http-remote ns as an inspiration

Gleb Posobin 2020-12-18T21:12:06.139600Z

Yeah, I saw that. Thank you!

👍 1
Gleb Posobin 2020-12-18T21:12:51.140Z

One thing that made me choose fulcro template vs fulcro rad template was that the latter doesn't have workspaces.

👍 1
Jakub Holý 2020-12-18T21:12:59.140200Z

Also, there is nothing that forces you to store the token in the DB, you could just put it in an atom. Everything is possible 🙂

Gleb Posobin 2020-12-18T21:14:05.140500Z

Yeah, but I want the whole state to be in one place.

Gleb Posobin 2020-12-18T21:14:33.140700Z

Though I guess I am influenced by re-frame in this regard.

Jakub Holý 2020-12-18T21:15:45.140900Z

Why not just add form-options/default-value to the attribute and make sure you formatters/... use it, if they dont out of the box?

Gleb Posobin 2020-12-18T21:15:49.141100Z

Also the fact that RAD is in an alpha was off-putting. What are the advantages of RAD?

Jakub Holý 2020-12-18T21:16:38.141300Z

in save-middleware you could check value != default and skip safe if it does =

Jakub Holý 2020-12-18T21:19:37.144500Z

@wilkerlucio hi, it seems that Index Explorer in Fulcro Insepct is not working in RAD. If you have by a chance fixed that for yourself, please share the fix 🙂 (I have defined an index-explorer resolver and added it to the parser but it fails because transit cannot handle values such as Fulcro attributes, which I assume are included as resolver functions in the index.) I do not intend to bother, just checking if a solution exists already 🙂

Tyler Nisonoff 2020-12-18T21:20:13.144600Z

i frequently am using the value in business logic, say calculating results and such.

Tyler Nisonoff 2020-12-18T21:21:00.144800Z

I could use formatters / or have a helper function that i remember call every time, but then im constantly deconstructing the props and needing to remember to wrap the value in a function

Gleb Posobin 2020-12-18T21:21:39.145Z

And that RAD has its own book: the fulcro book itself is long enough and hard to understand so far, and here I need to read another one! Scary 😃

wilkerlucio 2020-12-18T21:25:51.145600Z

hello, I've not tried with RAD, but if you can remove the fulcro attributes stuff from the index, it should work fine

phronmophobic 2020-12-18T21:44:14.149900Z

I'm still in the process of trying to grok fulcro's design, mainly how the db should be structured and manipulated • should all data in the db be normalized? Are there use cases where data shouldn't be normalized? • I see :db/id and :my-entity/id in the docs. is :db/id special? when is each appropriate? • If I have a component and want to test just that one component. Since the normalized db with the data for that component would be nested, is the right way to set that up to wrap the component I would like to test in a Root component that grabs a single entity that matches the test component and passes it to the test component?

Michael W 2020-12-18T21:46:06.150100Z

(pc/defresolver index-explorer [{::pc/keys [indexes]} _]                                                                
  {::pc/input  #{:com.wsscode.pathom.viz.index-explorer/id}
   ::pc/output [:com.wsscode.pathom.viz.index-explorer/index]}
  {:com.wsscode.pathom.viz.index-explorer/index (com.wsscode.pathom.core/transduce-maps
                                                  (remove (comp #{::pc/resolve ::pc/mutate} key)) indexes)})

Michael W 2020-12-18T21:46:57.150400Z

Someone gave me that to strip the resolve and mutate functions, I don't remember who

Jakub Holý 2020-12-18T22:04:11.150600Z

awesome, thanks!

Jakub Holý 2020-12-18T22:07:46.150800Z

1. Mostly yes but not necessarily. You don't need to normalize if you know the entity will not be used from multiple places. (Simply do not give it an :ident -> no normalization). 2. No, :db/id is not special in any way. 3. What kind of testing do you meant? Manual? Automated? Look at https://github.com/awkay/workspaces for displaying components in isolation

phronmophobic 2020-12-18T22:11:56.151200Z

regarding 3) I guess manual testing, but mainly as a way to support repl driven development

phronmophobic 2020-12-18T22:13:44.151600Z

my intention is to be able to start with a blank component and build it while the app is running

phronmophobic 2020-12-18T22:15:22.151800Z

workspaces looks really interesting. thanks for the pointer! my current goal is trying to make a desktop app with Fulcro for state management and my project, membrane, for graphics so I'll have to play with it to see what I can reuse.

Jakub Holý 2020-12-18T22:21:24.153400Z

FYI https://github.com/fulcrologic/fulcro-rad-demo/pull/18

phronmophobic 2020-12-18T22:24:21.155900Z

Is there a good example of a reusable form control in fulcro. Something like a date picker or custom drop down menu or something like that? I'm working on using fulcro for a desktop app, but don't want to rely on stateful OO components like textareas from Swing/JavaFx, etc. It would be possible to hide that state behind an object, like the DOM does, but I would prefer to use fulcro for all state management including cursors, focus, etc.

Jakub Holý 2020-12-18T22:24:22.156Z

Ok! Yes, you could put the component under Root and use an ident join to grab the entity - Root's :query [.. {[:person/id 123] [:person/name ..]}] and in the body something like (ui-my-component (get props [:person/id 123])) could work (notice it is get, not get-in, same as for df load markers)

phronmophobic 2020-12-18T22:25:37.156200Z

perfect! that's what I figured, but wasn't sure. It seems like it would be possible to wrap that behavior in a function that creates an anonymous "Root" component for just that purpose

phronmophobic 2020-12-18T22:26:55.156400Z

as an example:

(defsc Checkbox [this {:checkbox/keys [id checked?]}]
  {:ident :checkbox/id
   :query [:checkbox/id :checkbox/checked?]}

  (ui/on
   :mouse-down
   (fn [_]
     [`(toggle-checkbox {:checkbox/id ~id})])
   (ui/checkbox checked?)))


(def test-app
  ;; pop up a window with just a checkbox
  (quick-view! Checkbox {:checkbox/id 1
                         :checkbox/checked? true}))
I've been using this, but it currently creates a non-normalized db which breaks things when I go to a normalized db. I think I can fix quick-view! to have my cake and eat it too

tony.kay 2020-12-18T22:35:51.156900Z

As you’ve seen, you can do it. But here’s where it will cause you problems (which are the same problems for putting other code artifacts in the db): 1. If you make such a tx full-stack the fn will cause exception (live code cannot be serialized) 2. Inspect won’t understand them, or be able to display much sane 3. It breaks the overall conceptual design: operations are data in fulcro. Save a muation symbol and params to state instead. This leads to conceptual complexity in your program that is usually quite easy to avoid. 4. If you wanted a support viewer (serialize db to devs so they can diagnose errors from the field), the such values will make db not make sense. That said, F2 is harder to work with than 3 in certain circumstances (see Incubator in fulcro-legacy for some of the add-ons that inspired F3). Sometimes you just want a callback, but Fulcro does encourage you to formalize those into mutations..and a mutation (symbol + params) is something you can put in state.

1
tony.kay 2020-12-18T22:37:41.157100Z

Short answer: Make a ns for an atom to hold your app. This is necessary to avoid circular refs (clj limitation). Then, when you create your app, make sure you initialize that atom. Now your app is globally accessible. Write your remote to grab the app from the atom and do whatever it needs to do.

👍 1
tony.kay 2020-12-18T22:38:56.157300Z

You could also make an atom just for the auth stuff (localized to remote) and have the client-side request/response middleware do the work of seeing the auth response, and sending it out on requests.

tony.kay 2020-12-18T22:39:00.157500Z

lots of ways 🙂

tony.kay 2020-12-18T22:40:58.157700Z

@tylernisonoff the approach I recommend is basically what you’ve discovered: Leverage pathom, make it a virtual attribute that behaves the way you want. Leverage save middleware to deal with the write complications that this incurs.

😁 1
tony.kay 2020-12-18T22:43:10.157900Z

that makes it super clean. The “hacks” are very well-localized. You could go one step further if this is common: Attributes are extensible, remember? 🙂 You can invent a new nsed key that you add to attributes where you want this sort of behavior. Value can be a lambda or a value (use !?). Then just copy the resolver generator code (it’s pretty small) and augment it to support your new general use-case. Same for write middleware.

Tyler Nisonoff 2020-12-18T22:44:52.158200Z

Ah yeah thats a neat idea, thanks!

tony.kay 2020-12-18T22:46:59.158400Z

Please (please) read an understand as much about the combo of query/ident/initial-state as you can. This video, which is very outdated (Untangled/Om Next days), is also perhaps useful for understanding the db ideas: https://youtu.be/mT4jJHf929Q

tony.kay 2020-12-18T22:47:50.158600Z

You should normalize anything you plan to modify, but there is no hard requirement. A “big blob of data” is allowed.

phronmophobic 2020-12-18T22:47:57.158800Z

I watched that video last night, but I'll try it again

tony.kay 2020-12-18T22:47:59.159Z

I personally normalize everything

tony.kay 2020-12-18T22:48:17.159200Z

except things like dumps of data that are just like points of a graph or something

tony.kay 2020-12-18T22:48:30.159400Z

report data, you know

tony.kay 2020-12-18T22:48:39.159600Z

non-editable, and much faster to just keep in a bundle

tony.kay 2020-12-18T22:49:08.159800Z

The other videos in the F3 playlist cover a lot of ground

phronmophobic 2020-12-18T22:49:40.160Z

I think I understand some of the concepts, but I struggle with groking the operations: merge!, merge*, db->tree, and merge-component

tony.kay 2020-12-18T22:49:56.160200Z

Welcome. The intended design of RAD is that you mostly outgrow it, but adopt the principles 🙂

👍 1
phronmophobic 2020-12-18T22:49:57.160400Z

I can't find any examples that just show example inputs and outputs

tony.kay 2020-12-18T22:50:19.160600Z

ignore merge!, merge*

tony.kay 2020-12-18T22:50:28.160800Z

I never use those…treat them as internal

tony.kay 2020-12-18T22:51:11.161Z

db->tree is essentially Datomic pull, for Fulcro basically (different API, but that’s the purpose: state + query => tree)

tony.kay 2020-12-18T22:51:50.161200Z

Think of merge-component as a non-network version of load!

tony.kay 2020-12-18T22:53:23.161400Z

The model is really really really simple. You have a tree of data and a tree of components (with queries). The two match each other in shape. merging Root query with Root data tree makes a normalized database. merge-component is how you add a “fragment” to that tree. load does network I/O, then uses merge-component to add fragments to that tree.

phronmophobic 2020-12-18T22:53:35.161600Z

there are several examples in the tutorial that modify the state of the db directly. I thought understanding what merge* does would help understand those examples

tony.kay 2020-12-18T22:53:39.161800Z

initial state is that initial data tree that is used to make the db

tony.kay 2020-12-18T22:54:17.162100Z

mutations have optimistic actions that can do anything to the db…but they are all technically graph edits.

tony.kay 2020-12-18T22:55:26.162300Z

The db format is trivial. top-level “root” keys that have any meaning you want, top-level keys (you define with the first member of ident functions) that represent tables indexed by id, entries in those tables, and “idents” (lookup refs) that are used as pointers to join it all together.

phronmophobic 2020-12-18T22:56:07.162600Z

so in the case of building a desktop app, should all of the edits be done in actions since there is no network?

phronmophobic 2020-12-18T22:56:15.162800Z

that's what I've been doing

tony.kay 2020-12-18T22:56:23.163Z

some utilities like the stuff in normalized-state ns might be better reading 🙂

👍 1
tony.kay 2020-12-18T22:56:44.163200Z

Yes, if you mean “mutation actions”

👍 1
tony.kay 2020-12-18T22:57:13.163600Z

it leaves open the possibility that you might actually want to talk on a network via Fulcro in some apps :)

tony.kay 2020-12-18T22:57:43.163800Z

reading the source of merge and db->tree is like reading the source of postgresql to understand what a table is

😆 2
tony.kay 2020-12-18T22:58:14.164100Z

if you succeed, you’ll darn well know what a table is, but it sure is a hell of a way to go about it 🙂

phronmophobic 2020-12-18T22:59:41.164400Z

makes me feel better about not understanding everything then

2020-12-18T23:55:41.164600Z

If you need to pass a bearer token instead of using session auth, I wrote a blog post describing one way to do that: https://chrisodonnell.dev/posts/giftlist/api_auth/