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-29T00:07:32.372800Z

Yes @jatkin, that’s about the idea. This is a case where the ui tree can vary slightly in shape from the query (them matching exactly is a simplification for new user understanding)

JAtkins 2020-12-29T00:09:25.373200Z

Cool, that works.

tony.kay 2020-12-29T00:10:09.373400Z

you also have access to comp/children, so you can get the notation of:

(ui-wrapper-whatever sub-wrapper
  (ui-some-component child))

tony.kay 2020-12-29T00:10:44.373900Z

non-computed factories send extra things as children

tony.kay 2020-12-29T00:12:54.374300Z

https://book.fulcrologic.com/#_children

tony.kay 2020-12-29T01:07:45.374500Z

Right, it is a little bit of a misnomer, but it is really hard to come up with good names. The vast majority of components you write in Fulcro will have queries, and therefore state in the database. Technically pre-hooks such components always have access to component-local state, so you can say that the name is actually completely accurate for the time period in which it was named. If you don’t need any kind of state, then what you want is a function. However, now that hooks are here it is possible you might want to define a component that is just a function (presumably for side effects), and while there is usually some kind of state involved it is technically possible to make a thing that has no concept of state…but using defsc for hooks also implies that you are going to use a query for that case…otherwise why not just write a function?

tony.kay 2020-12-29T01:10:38.375200Z

Has anyone tried the new guardrails async mode? If so, did you notice a runtime dev speed improvement?

tony.kay 2020-12-29T01:10:50.375500Z

(only helps clj, unfortunately)

Christopher Genovese 2020-12-29T04:28:50.375600Z

Sorry to continue with this @tony.kay, but I want to make sure I am clear on the options. It seems that to use RAD my choices are to write a konserve backend for mongo (and integrate it with kvstore) or create a more direct RAD adapter. Given that time is a nontrivial issue here, I need a simple functional starting point, so I'm inclined towards the latter. What is the minimum interface that an adapter needs to support? It's not really clear to me from the existing adapters or the RAD book. I can easily get data into clojure in a nice form and can write Fulcro resolvers for it, but I'm not sure what the easiest way is to connect these to the RAD magic. Thanks for your help!

Gleb Posobin 2020-12-29T06:09:31.375800Z

What about the first page load? I am not using SSR.

Gleb Posobin 2020-12-29T06:11:14.376Z

I can live with the delay between when the component is rendered and when it learns its route params, just curious.

Gleb Posobin 2020-12-29T06:13:01.376200Z

Hmm, though not sure why the component gets rendered before the path param is loaded if I call dr/target-ready in the mutation after the swap! on state that adds the param.

tony.kay 2020-12-29T06:54:43.376500Z

There isn’t really much magic on the back-end at all. You have to have resolvers that can resolve queries. The generated resolvers in Datomic are really trivial when you use the recommended approach: id in, attributes out. The main thing an adapter really does is save, which is just a conversion of a normalized diff (see Fulcro form-state) to a database write. Really that’s about it. In Datomic that’s just a few hundred lines. But, since the normalized diff is a complete description of a graph change, it gives you a fully-general write mechanism. At that point you can write your server-side mutations in terms of form save (manually generate a diff and pass it to the form save, which in turn will use the db adapters and distribute the values to the corrected db(s)).

tony.kay 2020-12-29T06:55:17.376700Z

The normalized diff is trivial: {[:person/id 3] {:person/name {:before "Bob" :after "Joe"}}}

tony.kay 2020-12-29T06:56:30.376900Z

From there an adapter could support additional helpers. E.g. schema generation, schema validation, reverse eng tool -> attributes, etc. etc. But really the only things the front-end needs are the ID-based resolvers and diff->txn functionality.

tony.kay 2020-12-29T07:12:23.377100Z

so, the ideal pattern for a production-ready first page load, IMO, is to have the html render something that shows a loader. The app loads with a flag that is false in initial state (:ui/ready? for example). The rendering when that flag is false is exactly what is already in the app div the HTML generated. So, the user doesn’t really see the transition from static HTML to running app. You do your initialization, route, and then some aspect of your final initialization (which might be route-deferred) changes the flag to true. Now your app renders, and you’re right where you want to be. You are making a frame-by-frame animation via state. Fulcro does not know what you want to show when. It just renders each frame as it happens. You’re ultimately in control of what renders. Dynamic routing has a central set of concerns: composable UI switches that let you have some control over what happens as things come and go from the screen. Nothing’s perfect, and there are certainly imperfections in D.R. I initially wrote that because ppl were complaining they didn’t know how to go about making a ui router, and so I built that as an example, but it was good enough that I released it. It’s since gone through some revision, but it still isn’t ideal in every way. Fulcro’s rendering, as I’ve said at least 3x this week alone, is very very naive: mutation -> render -> mutation -> render. Component’s don’t have much control over when they render, only what. The state machines behind D.R. essentially run mutations to move from state to state, meaning that some (unintentional) frames of state might be rendered…this is where some refinement in the internals would be nice. That said, my expectation is that if you return a route-deferred then that route will not be visible until you say target-ready. The route params are passed in before you can even say route-deferred, so I honestly don’t know what you’re talking about, unless I’m misunderstanding you’re description, or your code doesn’t match your description.

2020-12-29T09:24:02.377300Z

Stepping through state transitions with fulcro inspect can be a good way to gain insight into what is happening in your app.

wilkerlucio 2020-12-29T11:44:46.379Z

I personally use a lot of defsc to define UI root components, they usually dont have any query but I want defsc to use the fulcro css system

Jakub Holý 2020-12-29T15:03:30.382Z

I would very much appreciate if you could provide your feedback on https://github.com/holyjak/blog.jakubholy.net/blob/master/content/asc/posts/2021/fulcro-divergent-ui-data.asc, which talks about: > Fulcro’s stateful components serve normally both as elements of the UI and to define the data model, i.e. data entities with their unique IDs and attributes. And that is what you want 95% of the time. But what if your UI and data model needs diverge? > We will take a look at what different kinds of divergence between the UI and data entities you might encounter and how to solve them. I am primarily interested in: 1. Is everything correct? 2. Have i forgotten some cases? 3. Any [better] examples / code samples to add? 4. Is it understandable to people "new" to Fulcro? 🙏

Tyler Nisonoff 2020-12-29T15:33:22.382400Z

This is wonderful!

Tyler Nisonoff 2020-12-29T15:36:27.382600Z

One thought: I think I would have groked it slightly quicker if the query / idents for PersonView / PersonDetails were shown in “A Data-only component” and similarly PersonIdentification in the last section — you implied that PersonIdentification queries for person/name person/email in the explanation after, but would be nice to see that in the queries of the components But also want to emphasize that I think its is a great blog post 🙂

1👍
tony.kay 2020-12-29T15:36:43.382800Z

I’m adding comments via https://web.hypothes.is/ @holyjak

1❤️
Jakub Holý 2020-12-29T15:39:42.383400Z

I have never heard about ☝️. I suppose you will share a link to your comments eventually, or how does it work?

tony.kay 2020-12-29T15:40:51.383600Z

I made the comments public. You install the extension and just go to the page.

tony.kay 2020-12-29T15:41:23.383800Z

it’s super handy for giving feedback about arbitrary web content

tony.kay 2020-12-29T15:42:35.384Z

@wilkerlucio do you even mount those, or just use them for a place to put namespaced CSS?

Jakub Holý 2020-12-29T15:43:21.384200Z

Awesome, thanks!

wilkerlucio 2020-12-29T15:43:30.384400Z

yes, I mount them, they are usually the design system part of the application, buttons, checkboxes...

wilkerlucio 2020-12-29T15:50:44.384600Z

example case in Fulcro inspect code: https://github.com/fulcrologic/fulcro-inspect/blob/master/src/ui/fulcro/inspect/ui/core.cljs

tvaughan 2020-12-29T15:59:09.385Z

> Have i forgotten some cases? Let's say you have two data-only components that only differ in their query namespaces, e.g. pdf/name and pdf/size versus png/name and png/size, and you want to render these the same, e.g. (div (h2 name) (h4 size)). Comments about the naming convention aside, would this fit?

tony.kay 2020-12-29T16:06:32.385300Z

oh, you don’t mean “root” components. I think you mean “stateless leaf” components 😉

tony.kay 2020-12-29T16:07:01.385500Z

root to me means something you pass to mount

Christopher Genovese 2020-12-29T16:08:41.385800Z

That's very helpful. I appreciate it!

Jakub Holý 2020-12-29T16:24:07.386Z

@tvaughan Thanks! Perhaps. Could you elaborate more the problem you have encountered? Why data-only components in this case? And are you looking at how to avoid having to manually defsc for each of the file types or what? Thanks!

tvaughan 2020-12-29T16:32:23.386200Z

I was thinking of abstracting away the body of the defsc, i.e. have each defsc call a shared function that returns (div (h2 name) (h4 size)). Would this be the right approach, or is there an alternative I haven't thought of?

wilkerlucio 2020-12-29T16:49:57.386400Z

sorry, bad naming on my part

wilkerlucio 2020-12-29T16:50:03.386600Z

yeah, not root

tony.kay 2020-12-29T18:05:52.387200Z

@tvaughan Yes of course. It’s all just function calls anyway. If there’s a common layout/formatting concern there is no problem at all putting that in a function, or even a component without query if you want shouldComponentUpdate optimizations.

tvaughan 2020-12-29T18:06:22.387400Z

Good to know. Thanks @tony.kay

Jakub Holý 2020-12-29T21:14:25.387600Z

Tony: I have stolen your m/returning auth/Session example 🙂 https://github.com/holyjak/blog.jakubholy.net/commit/e6991515c57f2d44f2ae5aff90c4a4cbd6e3b039

genekim 2020-12-29T21:47:11.392900Z

Really great article, @holyjak —  Last night, I finally watched the 5+ remaining hours of @tony.kay Fulcro 3 videos at 1.25x speed, with the hopes of the big picture sinking in via osmosis. :). I went to sleep exhausted, but hopeful that clarity will eventually emerge later this week. 😁. (Eager to sit down in front of IDE and see if I can actually build something using what I've learned!) @holyjak — I love the work you're doing helping describe the critical Fulcro concepts!

1❤️
tony.kay 2020-12-29T22:00:04.393Z

that’s fine 🙂

Michael W 2020-12-29T22:32:46.393500Z

Towards the beginning when you talk about defining the data entity, could you expand on static ident and dynamic ident, what they are, what they do, and possibly when it's appropriate to use them?

Michael W 2020-12-29T22:37:14.393700Z

That's the only thing I was confused about in the entire article, thanks for the excellent content.

phronmophobic 2020-12-29T22:51:07.396900Z

Achievement Unlocked! I made a simple todo app for desktop and terminal using fulcro. It's not going to win any awards, but I think it's a good start. The code is available https://github.com/phronmophobic/membrane-fulcro. Thanks for everyone's help, especially @tony.kay! It's still a little rough around the edges, but if anyone is interested in trying, feel free to ping me and I'm happy to answer any questions.

1👏3🤯3🎉
Jakub Holý 2020-12-30T09:07:10.411900Z

I would like to add it to Awesome Fulcro if you are OK with it, see https://github.com/holyjak/awesome-fulcro/pull/4/files

phronmophobic 2020-12-30T19:24:50.431300Z

Absolutely!

phronmophobic 2020-12-30T19:26:18.431500Z

you may have to update the tag line: > the most awesome webapp framework the Earth has ever seen. fulcro can do more than make web apps 😉!

1😻
phronmophobic 2020-12-29T22:52:24.398400Z

phronmophobic 2020-12-29T22:52:30.398700Z

5❤️
Jakub Holý 2020-12-29T23:08:35.399Z

Hi, thanks! What I call a dynamic ident is one based on the props so (fn [] [:person/id (:person/id props)]) or, in the template form, simply :person/id

tony.kay 2020-12-29T23:12:53.401200Z

I absolutely love the terminal version. I cut my coding teeth in C w/Curses in HP-UX and Solaris in the late 80's/early 90's. Played a ton of nethack in the wee hours, and while it seems a bit silly to use a terminal for UI these days, I still love it.

1❤️
phronmophobic 2020-12-29T23:15:29.401400Z

At some point, I'd like to implement something like this, https://github.com/sindresorhus/terminal-image, for membrane even though it's completely impractical. Also, I only learned recently that terminal actually supports mouse input.

tony.kay 2020-12-29T23:18:58.401800Z

long gone are the days of the single-color cyan terminal running at 19200 baud (via local wires)

tony.kay 2020-12-29T23:22:10.402Z

1
tony.kay 2020-12-29T23:22:15.402400Z

oh yeah

Michael W 2020-12-29T23:26:09.402700Z

gotta love the null modem cable

tony.kay 2020-12-29T23:46:27.402900Z

complete digression, at one point I wrote an ncurses implementation of mine sweeper. The recursive clearing algorithm that opens up the board when you click where there are no mines is kind of fun to write, and if you set your terminal to 1200 baud with stty you could watch it move the cursor around and change the text. Modern terminals ignore the device setting, so not as much fun.

tony.kay 2020-12-29T23:46:51.403100Z

you have to resort to manual “sleep” calls

1😁