oh. funny. didn’t remember that
no one has tried this yet
😄
it works!
😄
see, not hard at all
what UI is that using, just Swing?
maybe JavaFX?
skia https://skia.org/
but it can render to a number of different targets, https://github.com/phronmophobic/membrane
right now, there's skia, swing, and terminal on desktop, but I plan on supporting javaFX at some point
nice
that is a really cool thing to combine with Fulcro
I agree
I had just about given up on integrating existing ui frameworks because, so far, they've been coupled to react
Fuclro seem great. I'm kicking myself for not trying it earlier!
@ 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.
😆 , poor other "Adrian Smith" that gets mentioned for random discussions I'm in.
@jatkin, will do! I'm working on creating an example repo for a proof of concept
lol
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?
it's not the first time this has happened 😁
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...?
It should only be used by cljs, and so it should only needed at build time.
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! 🙂
@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…
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 🙂
and glad you like it all…RAD is a bit unpolished and underdocumented, but I only have so much time
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?
The error is from putting a UUID on a form. the SUI plugin does not have an input renderer for that data type
Is the problem just aesthetic, or does it actually prevent any demo functionality?
no idea 🙂 Been a while since I looked
I wasn’t aware I had used a UUID in a form field
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
At least Fulcro Inspect will suffer as DB needs to be serialized to send to it I believe.
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
I’m not sure but I think it will show as an unknown transit type
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.
Maybe it is related to the diff
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?
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...
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.
Hmm, weird, I looked at rad template and it seemed much less fully-featured.
Ah, so I can access the app db externally.
Yes. And look at wrap-csrf-token
in the ...http-remote ns as an inspiration
Yeah, I saw that. Thank you!
One thing that made me choose fulcro template vs fulcro rad template was that the latter doesn't have workspaces.
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 🙂
Yeah, but I want the whole state to be in one place.
Though I guess I am influenced by re-frame in this regard.
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?
Also the fact that RAD is in an alpha was off-putting. What are the advantages of RAD?
in save-middleware you could check value != default and skip safe if it does =
@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 🙂
i frequently am using the value in business logic, say calculating results and such.
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
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 😃
hello, I've not tried with RAD, but if you can remove the fulcro attributes stuff from the index, it should work fine
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?
(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)})
Someone gave me that to strip the resolve and mutate functions, I don't remember who
awesome, thanks!
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
regarding 3) I guess manual testing, but mainly as a way to support repl driven development
my intention is to be able to start with a blank component and build it while the app is running
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.
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.
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)
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
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 tooAs 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.
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.
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.
lots of ways 🙂
@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.
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.
Ah yeah thats a neat idea, thanks!
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
You should normalize anything you plan to modify, but there is no hard requirement. A “big blob of data” is allowed.
I watched that video last night, but I'll try it again
I personally normalize everything
except things like dumps of data that are just like points of a graph or something
report data, you know
non-editable, and much faster to just keep in a bundle
The other videos in the F3 playlist cover a lot of ground
I think I understand some of the concepts, but I struggle with groking the operations: merge!, merge*, db->tree, and merge-component
Welcome. The intended design of RAD is that you mostly outgrow it, but adopt the principles 🙂
I can't find any examples that just show example inputs and outputs
ignore merge!, merge*
I never use those…treat them as internal
db->tree
is essentially Datomic pull, for Fulcro basically (different API, but that’s the purpose: state + query => tree)
Think of merge-component
as a non-network version of load!
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.
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
initial state is that initial data tree that is used to make the db
mutations have optimistic actions that can do anything to the db…but they are all technically graph edits.
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.
so in the case of building a desktop app, should all of the edits be done in actions since there is no network?
that's what I've been doing
some utilities like the stuff in normalized-state ns might be better reading 🙂
Yes, if you mean “mutation actions”
it leaves open the possibility that you might actually want to talk on a network via Fulcro in some apps :)
reading the source of merge and db->tree is like reading the source of postgresql to understand what a table is
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 🙂
makes me feel better about not understanding everything then
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/