Fork me on GitHub
Piotr Roterski07:01:42

hello 👋 Is it possible to reuse defsc-form definition data across different forms? Let's say I have two similar versions of a form:

  PostForm [this props]
  {fo/id post/id
   fo/title "New Post"
   fo/attributes [post/body]})

  PostReplyForm [this props]
  {fo/id post/id
   fo/title "Reply"
   fo/attributes [post/body]})
Their definition is just data, so, in theory, I should be able to share it so the common parts are reused and the differences are immediately visible. I tried doing something like this:
(def post-form-data
  {fo/id post/id
   fo/title "New Post"
   fo/attributes [post/body]})

  PostForm [this props]

  PostReplyForm [this props]
  (-> post-form-data
      (assoc fo/title "Reply")))
but it doesn't even compile and I'm not well-versed enough in cljc macro semantics to fix it. Is there another syntax that could work or would it require patching the defsc-form macro to make it work (if that's even possible)?

Jakub Holý (HolyJak)15:01:26

Error checking in the macro is little dumb in this regard. The solution I can think of is to make something like the merge fn but make it a macro (that just calls merge) - that way it is also evaluted at compile time and resolves into a {...} before the code inside the defsc sees it.


Yeah sorry, the macro has to be able to read the options at compile time, so general expressions are just not compatible. I could have it resolve symbols but that would make failures even more mysterious. It is possible I can fix that. If so I'll give it a shot sometime soon


So, the defsc-report macro definitely needs it to be a compile-time map, because it needs to know several things in order to emit the correct code. Forms, technically, at the moment, could allow an expression, but I’m not sure it makes sense to loosen that since then it would be inconsistent with report.


This is similar to defsc, which requires it be a map so it can do compile-time checking of various arguments.


You can always copy the macro code to your own code and loosen these restrictions. It’s a trivial change in the case of form.

Piotr Roterski18:01:56

thanks for the insights 🙌 I was kinda hoping for a magic solution so it's easy to fully benefit from data-driven rad's nature without loosing the restrictions. I feel a bit out of my depth playing with those macros, but maybe I'll come up with something that works for me.


So, the change in the form macro is easy


(if (map? options) (existing expression) options) and then the macro spec can have :options any?


specifically, when you copy the spec for the macro, change: line 306 to: :options any? and when you copy the function that processes the macro, line 493:

options      (if (map? options) 
                          (opts/macro-optimize-options env options #{::subforms ::validation-messages ::field-styles} {})

👍 1

The remaining work would just be fixing namespaces


My intention at some point is to have function versions of these that are pure user-space (not compile-time)…form is very amenable to that, but not sure about report. It’s on the TODO list for this year.

fulcro 1
❤️ 1

You should only need to copy those three things I think (spec for macro args (used to parse the args), defn defsc-form*, and the macro itself), and refer to everything else from form ns.


so it’s only like 50 lines


actually, there’s more. The form ns DOES do macro arg checking in other fns 😕


yeah…the query building requires being able to look at macro args at compile time


there just no way in the present code form to use expressions reliable, unless you want to deal with the complexities of resolve and evaluation at compile time, which is a HUGE can of worms that is usually not profitable


When I wrote this stuff originally I wanted good compile-time errors, but giving those errors requires the map be readable by the macros. The function versions (once I write them) won’t give compile errors (of course), but it isn’t as easy as I said above

Piotr Roterski18:01:25

> My intention at some point is to have function versions of these that are pure user-space (not compile-time) I'd much appreciate that. While it's not something I necessarily need, I think it'd be beneficial in terms of usability (reuse & visible diffs across versions). I'm trying your suggestions right now, but, long term, having to maintain my own macro/lib version might not be justified.

Piotr Roterski18:01:08

I just made those two changes on lines 306 and 493 in my local fulcro-rad's fork (to skip all the ns hassle) and it actually worked!


hm…I’m actually surprised because of that other function…but ok! 😄

😅 1
Jakub Holý (HolyJak)19:01:17

Wouldn't it be simpler to make the macro version of merge?


there’s really no such’d have to resolve the data, which means all sorts of trickery

Jakub Holý (HolyJak)20:01:53

Ah. I thought that if had a macro that merges two maps at compile time, I would get a map. I guess I have to study resolution rules.


I’m a bit unclear myself…it’s always something I have to tinker with because I don’t try to cross this boundary, because it is a pain, but imagine you’re compiling a CLJS file


then it is VERY clear: that def simply will NEVER exist where the macro expands


thus the complication: you’d HAVE to use CLJC files for that to work at all, and then it would be generally confusing about what the exceptoins are, etc.


and things like declare followed by a later def would be a problem, etc, etc


it’s madness to allow expressions in things that a macro is treating like syntax


But, I could provide a functional version that doesn’t do much checking, and that would be usable in this case


Give me a few…I’m seeing how hard that is…so far it looks pretty easy


@UHA0AQZ2M I got functional versions of the macros working and released. See release announcement.


I tried them out in place of the macros in the demo and they seemed to work. If you find any problems with them I’d appreciate a report. See the source. Really wasn’t much to it, but it does cause a loss of error checking. Also, the macros will tolerate the use of full attributes in some places as keys, and will rewrite them to keywords. The new functions do NOT do that…so for example the report form-links options allows this, but in the function version you MUST use keywords as the keys of form-links. Other options are similarly affected (if they accept an attribute as a key, they most likely will require a keyword in the function form)

Piotr Roterski07:01:32

awesome, thank you ! :medal:

Piotr Roterski08:01:43

I've tried it and it seems to be working in my app so far. If I see any problems down the road, I'll definitely report them


I'm trying to use Raw without React. The build fails with:

Build failure:
The required JS dependency "react" is not available, it was required by "cljsjs/react.cljs".

Dependency Trace:


You cannot use the components ns... Looks like merge is?? Just need to change that to use raw


Possible oversight on my part. Not sure why merge cannot use raw. I'll check


Thanks, Tony. Double-checked with developers guide. But as you can see, I'm just requiring raw.application.


yeah, I have a few refs to the wrong ns internally…I’ll fix those and release a patch

gratitude 2

The transaction processing ns also uses component…might take a bit more work.


Give me a few hrs


Not urgent at all, Tony.


Try 3.5.10-SNAPSHOT


There are a few nses that need non-raw (all of the routers, and the normalized-state helpers), but nothing critical. I had to add refresh-component! to the list of functions you have to supply. It is only internally used by synchronous transactions, so if you’re using RAW and sync, you’ll need to implement that for your rendering


It works 🙂 Thanks for the heads-up about refresh-component!.


Fulcro RAD 1.1.0 released to Clojars Version 1.1's main change is an upgrade of an external js dependency that was a little rough: js-joda. The date-time formatting was manually rewritten to be CLJC to use browser Intl instead of having to have the (rather large) js-joda localization file. This means the internationalized date formatting has a MUCH smaller build footprint, but IF you use localized date PARSING that requires a locale (e.g. isn’t just numbers), AND you are using the RAD data-time namespace to build a DateTimeFormatter, then you need to switch your code to use directly instead.

❤️ 1

RAD 1.1.1 just released as a follow-on due to user requested feature: There are now non-macro functions to create reports and forms (report/report, and form/form). These functions take the same options map as the macros, and allow an optional render body function. The main difference is that they generate the RAD form/report at runtime. This also allows you to use expressions for the options maps.



(defsc-form MyForm [this props]
  {fo/id blah/id
can be written as
(def MyForm
  (form/form ::MyForm
    {fo/id blah/id
but more importantly the options map in the second case can be an arbitrary expression.

🚀 2

Caution: The macros do some error checking and magic for you that is lost when using the functions. In particular the macros try to let you use attributes in certain map keys (e.g. ro/form-links {thing/nm Form}). This is fixed up by the MACRO ONLY, and will not work in the function version. You must instead use they keyword (i.e. ro/form-links {:thing/nm Form}). Contributions welcome if anyone wants to port over the error checks and key morphing.