fulcrologic / fulcro Goto Github PK
View Code? Open in Web Editor NEWA library for development of single-page full-stack web applications in clj/cljs
Home Page: http://fulcro.fulcrologic.com
License: MIT License
A library for development of single-page full-stack web applications in clj/cljs
Home Page: http://fulcro.fulcrologic.com
License: MIT License
Normalize form state
write bigger examples with custom commits
The ^:once notation on defui is necessary for hot code reload to work properly. I could make it the default, but that would technically change the behavior of it (and the ui
macro). I personally have not used either in a way where I would need the reevaluation to happen.
Several users have indicated a desire for it to be the default, but I'm always leery of changing something like this in a global way without considering other possible courses. One would be that we could use a JVM property instead, so it would be the default, say, if the -Dhot-reload property was set on the JVM. That would eliminate the need for it in code, but would possibly confuse users where it didn't work because they forgot the JVM option.
So, while I see a way around changing the default (use a JVM option), and this would also maintain 100% source compatibility, I am tempted to just make the ^:once
behavior the default.
Thoughts anyone?
Not sure if this works or not, but it is definitely critical that nesting dynamic queries inside of static query components should work, and vice-versa. Composition breaks otherwise.
I think the requirement might be: If you use dynamic queries, you have to compose them to the root...but I think it is possible to use static queries within the tree as static branches.
If I can figure out a way for them to compose seamlessly in either direction, that would be ideal.
They are being marked "not found" and swept.
In the Fulcro devcards it's mentioned how FormatJS is extensible; however, I don't see any way to pass a formats object to MessageFormat. As I understand it, the MessageFormat constructor can take the formats object as the third parameter, but fulcro.i18n/trf
is hardcoded to only pass two. Could we get an option in new-fulcro-client
or an atom in fulcro.i18n
where we can set custom formats?
Something is causing Om's statics to not get properly marked during adv optimization. This issue is to track hints as I discover things.
The new code splitting support makes it relatively easy to make a dynamic routing system that can be hooked into the existing router infrastructure to facilitate breaking apart large applications. I've written a demo for how to basically structure the router for this dynamic addition, but it needs to be codified into the library itself.
Forgot to pass reconciler on line 250 of routing.cljc.
The validate forms mutation seems to be corrupting the form on template. Could be a bug on the template, or a bug in forms support
Merging into the root node of the graph is possible, so perhaps we should allow queries that have no top-level ident to be the target of m/returning
.
Pros: you could define an arbitrary query that could update multiple things in the root, which are easy to query via link queries.
Cons: You can easily pollute/overwrite things in the root node, where accidental collisions are too easy
In the quick tour the grand total sum is supposed to update immediately due to a follow-on read, but it lags until the network request returns, indicating that it got queued with the network refresh instead of the optimistic update.
So, I've advocate that people use load
to take advantage of post-mutations in order to do blocking UI on full-stack mutations. This is wasteful in that it requires at least a server ping in order to see the result...it is needlessly chatty.
My hesitation to add something akin to a callback is that it throws async into the UI, and Om Next went to great lengths to get it out of there.
I've already let post-mutations in, which are a break with the model, though they are limited to data transforms. This prevents chains of callbacks, but sorta has the flavor of a callback.
I've been trying to figure out how to modify mutations so that one can actually do something on the successful completion of a remote mutation operation.
Fallbacks are fine for errors (though I don't believe they are practical).
In fact, I don't really think callbacks on mutations are really as practical either; however, some people would like to code blocking full-stack operations, and the only clear way to do that is to provide some mechanism where we have a sequential operation list that is full-stack aware:
Do f
, wait for a result, then do g
.
For loads, this is post-mutation
. A callback-feeling thing that I only accept because it is aimed at data manipulation of the loaded result. The only reason it is a mutation is I don't want to store functions in app state. This seems a perfect exception, even though it looks a bit callback-y
In mutations, we've not had a way of saying anything about sequence of full-stack interaction, even though there are queues that we can leverage to enforce it.
So, I'm exploring ways in which we can add this, as it would be useful.
The dynamic routing code, as written, needed to set a component query. This involves Om Next updating the application state with the new query. Unfortunately, the new update-routing-links for composition puts you inside of a swap that has already saved the state of the world for funcitonal update, so we're causing a nested swap that will be un-done after the code has completed. Thus, the queries that are supposed to be stored in app state never get stored there, which will lead to UI issues on refresh.
Add some kind of hook for cases where we know things are possibly not recoverable: E.g. a network error caused dynamic routing code loading to fail. Basically allow the program to transact, or js/alert, or something that let's the user know things are hosed, and what can be done.
The post mutation and fallback environment is insufficient for some tasks, and should include:
:ref
of original component that triggered load (if available):reconciler
:load-request
that contains all of the significant portions of the original load request (e.g. server-query (as focused/composed to send to the server, with params), keyword-or-ident, component-query, post-mutation, post-mutation-params):triggering-component
and :query-component
. These could be passed via metadata internally. I'm on the fence about this, because the temptation for abuse that can cause issues is high.It looks like Om schedules renders that can cause queries...verify this, and decide if it should just be a forced update.
After some discussion in #28 it became apparent that there are a couple of tweaks that would allow us to handle general errors in much more useful and advanced ways. See the comment stream in that issue for background, and add comments here to extend that conversation.
List of different kinds of things on the left that all share some same basic props (like label and type) so they can be rendered by one kind of UI thing, but detail screen on right uses router to show the right kind of edit screen.
After talking to Mitchel, he recommended a good pre-written controllable lib component. I think it is a better idea for me to show how to integrate that into the template instead, since re-use from a library is a primary concern, and I don't need to fiddle around writing something that already has a good solution.
If you load a dynamic route and it needs to load the module, only the query for the dynamic router get's updated and not the property ::routing/pending-route
this should get a read after a route has been loaded. The current behavior is that it will show a progress spinner indefinitely when a route has been loaded.
When combining libraries I seemed to have lost the server docs. I need to make sure those are integrated, and proofread them and improve them. Should also make sure the template shows how to do it...@Pancia, where is that code for template...all I see is some untangled-server stuff. Have you pushed it?
An optimization would just be to refresh all components of that class, even with diff qualifiers. Would not require another index, and we can treat adding an index as a future optimization if needed.
Fulcro 2.0 is no longer tied to the internals of Om Next, and can now add support for mutations that can list what keys they are changing so that follow-on reads will largely be a thing of the past at the UI layer. They will still be allowed, but will no longer technically be required.
As far as I can tell this was an optimization based on the idea that a mutation might be changing things that the UI currently doesn't care about, and that triggering reads for extra stuff caused extra overhead.
The thing is: there are indexes, you don't change that many things, and if they changed, you want to queue the follow-on reads for correctness. If the index lookup comes up empty it really didn't cost you that much.
I think we can do this with some special key on the mutation return. Om Next allowed an unused (for documentation only) key called :keys
on a mutation to advertise what it changes, but I've never really seen anyone use it because it was kind of obscure, poorly documented, and didn't actually do anything.
(defmutation do-thing [{:keys [id] :as params}]
(action [env] ...)
(refresh [env] [:person/name [:person/by-id id]]))
The SQL graph demo shows the problem.
There are cases where the config component throws exceptions that are very hard to diagnose.
Currently load markers are not put in the UI until network processing starts; however, this causes flicker. Change the load mutation to have an "action" part that will fix this.
In forms.cljc
:
`(log/warn ":onChange is not a function")`
Slight problem that this message is emitted when onChange
is not supplied. We should only emit the message if it is not nil. Something like this:
`(and onChange (log/warn ":onChange is not a function"))`
The workaround is to make sure tools-namespace is around when you're doing advanced compilation.
The fix is to figure out how to keep Closure from removing the static
methods on the generated React classes.
Some possibilities:
static
...perhaps it should just be metadata in cljs and clj, but that means some internal API changes that could break existing programs (anything that has been trying to call those statically against the classes)I need to finish making the devcards stuff work with state maintenance, and push it as a PR to devcards, so we can remove the hard dependency on devcards.
I've been wanting to build something for a long time that would give you a debug overlay in develop mode. It really would not be that hard to make. Basically a Fulcro app that is pre-packaged in the Fulcro resources that you can embed in your development HTML and link to your running app. All sorts of cool stuff possible. Table browser, state watches, etc.
The current implementation causes a number of problems with Fulcro. The real design should be this:
Put the load markers in a table, with the marker name as the key. Then the query becomes [:prop-of-parent [:fulcro/load-markers :my-marker] :other-props]
, and the destructuring can just be (let [{:keys [my-marker]} (om/props)] ...)
Now we can do a defui
for the LoadMarker, supply a query (and ident) function…perhaps even use a multi-method in render with a default. Now you have an easy way to customize the rendering of the marker. The query becomes [:prop-of-parent {[:fulcro/load-markers :my-marker] (om/get-query fulcro/LoadMarker)} :other-props]
and you get a factory like ui-load-marker
Things like fulcro-inspector cause log message to intermingle with app logging. It would be useful to be able to target logging by app so that the logs could be better controlled. Need to investigate possibilities.
Verify that this is an unused feature...pretty sure it is. Love to hear from people on their thoughts about it. I'm leaning towards deleting it from the system altogether. I just don't see it fitting in the picture very well.
When using dynamic queries Om Next keys the query by component (or reconciler) in app state. This means that if you use dynamic queries you'll lose support viewer support unless there is some way to create a serializer for this that can reliably save/restore these.
Doing background load demo. parallel throws an error.
The old lein plugin for extracting strings and generating modules for translations is out of date.
The mutation supported from on-form-change can modify anything in app state, but it cannot specify the data that has changed so that components outside of the form can refresh. This might be fixed in 2.0 with mutations that mark their reads, but for 1.0 it needs a parameter
shadow-cljs is another tool for developing and bundling ClojureScript. There are several features related to the GettingStarted document:
To define a compilation like https://github.com/fulcrologic/fulcro/blob/develop/GettingStarted.adoc#11-project-file , it's uses a file called shadow-cljs.edn
:
{:source-paths #{"src/main" "src/dev"}
:dependencies [[org.clojure/clojure "1.9.0-alpha17"]
[org.clojure/clojurescript "1.9.908"]
[org.omcljs/om "1.0.0-beta1"]
[fulcrologic/fulcro "1.0.0-beta10"]]
:builds {:dev {:target :browser
:output-to "resources/app/js/app.js"
:output-dir "resources/app/js/app"
:main cljs.user
:asset-path "js/app"
:devtools {:after-load cljs.user/refresh
:preloads [devtools.preloads]}}}}
Fields look similar to the one in Lein. But it's shorter to write since it's designed for compiling ClojureScript only. Many of the dependencies have already been built into shadow-cljs.
To start running shadow-cljs, you will need Node.js :
yarn add --dev shadow-cljs
yarn shadow-cljs watch dev # watch for development, dev for :build-id
It's started with a command line, so no need to add another script. As the watch server started, you get hot code swapping from :devtools
configs.
If you need a REPL for connected environment, try:
yarn shadow-cljs cljs-repl dev
After you got an HTML, to serve the files, it's built in shadow-cljs. Just add configs in :devtools
configs:
:http-root "public"
:http-port 8020
and you will get an HTTP server serving public/
at http://localhost:8020
.
I guess shadow-cljs can be faster, since it's designed for ClojureScript specifically. But it may be used together with lein too.
Besides, shadow-cljs brings some improvements to cljs development too, like CommonJS support, more work on externs, filename hashing, dynamic loading, etc. It brought me a lot of useful features. I hope it may help in your scenarios too.
Also, add an example to Pedestal's guide as a PR
The new history system will enable a cool kind of error handling that will simplify apps. It will be opt-in, since there are server requirements you must agree that you meet:
Currently mutations can return a value, but that value is ignored. The latest query syntax supports a mutation join like this:
[ { (do-something) (get-query Thing) } ]
which is a mutation joined to a subquery. This enables (syntactically) the possibility of having the client known how to merge the return value of the mutation.
The assumption for this is that the returned data from the mutation looks like props for the given Thing
(which must have an ident). The query itself will be annotated with metadata so that the ident can be generated from the combination of Thing
and the returned mutation value, meaning that we can merge/normalize the result.
I've talked a bit to @wilkerlucio about how to support this syntactically, and there are some things to think carefully about before we implement it:
Some ideas:
(defmutation do-thing [params]
(remote [env]
(returning env Thing)))
and perhaps one or two other helpers against env
, like:
(with-params env params)
- alter the outgoing remote mutation to use the given params
(returning env Component)
- alter the outgoing remote mutation to accept the given kind of component's data as a return value
and let them thread:
(-> env (with-params {:x 1}) (returning Thing))
Some other ideas that deserve consideration:
My current proposal is:
with-params
/returning
)Ident
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.