This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-09-01
Channels
- # aleph (7)
- # bangalore-clj (1)
- # beginners (89)
- # boot (5)
- # clara (21)
- # cljs-dev (2)
- # cljsrn (57)
- # clojure (58)
- # clojure-austin (1)
- # clojure-conj (1)
- # clojure-italy (5)
- # clojure-losangeles (3)
- # clojure-russia (4)
- # clojure-sanfrancisco (4)
- # clojure-spec (31)
- # clojure-uk (67)
- # clojurebridge (4)
- # clojurescript (56)
- # cursive (92)
- # data-science (4)
- # datomic (15)
- # emacs (23)
- # events (1)
- # fulcro (121)
- # gorilla (2)
- # jobs-discuss (1)
- # juxt (1)
- # lambdaisland (6)
- # lumo (13)
- # off-topic (11)
- # om (1)
- # onyx (17)
- # overtone (5)
- # parinfer (9)
- # planck (3)
- # re-frame (21)
- # reagent (95)
- # ring-swagger (7)
- # spacemacs (58)
- # vim (13)
- # yada (2)
https://github.com/fulcrologic/fulcro-template/pull/6
@tony.kay ☝️
Small fix to fulcro-template
@roklenarcic remember that your query is a graph, you should always be able to make paths do reach your data needs
@tony.kay do you have a suggestion on how to provide the app for the network layer now that it got removed from the start interface? my current solution is to initialize the atom before with a nil value and then reset! it right after, like this:
(defonce app (atom nil))
(defonce start-app
(reset! app (fulcro/new-fulcro-client :networking (some-network "url" app))) ; <- init before so this variable is available here
but I think this a bit ugly, wonder if you have an alternative way to handle this
@wilkerlucio I’m putting the networking in an atom, and passing the completed app from started-callback into it. Networking is a bugger…Om needs it to start, but some networking things need the complete app in order to update the UI, etc. Either way. Circular reference needs are never pretty, but it’s why I had to remove the parameter…the app you were getting wasn’t done initializing. There was no way to resolve it “inside”…well, a more significant API change would be necessary. It’s on the low-prio todo list.
@roklenarcic Load actions queue up reads. Reads are combined into one network call if possible (non-conflicting keys)
as do load calls. AS long as you have the thread, any number of loads will attempt to coalesce into one larger read.
The state swap is an optimistic (immediate) update. Loading takes whatever time loading takes. As @wilkerlucio said: if you need to work on the result of a load, use a post-mutation which is a parameter to load-action.
@tony.kay thanks, that's ok, I was just making sure I wasn't missing some other way to do it, this one is currently working for me, I need the app in order to do those "remote jumps" I told you before
Yeah. File uplaod networking needs it for progress updates, and websockets need it for server push. It’s why it got added in the first place, but the person that did that work didn’t realize they were creating a dependency loop where one or the other would break eventually.
I probably should have made a better fix when I was touching it. It really just needs a two-phase initialization. I probably could have just changed the app parameter to an atom, and told you to keep the atom, so that later stages in init could have updated the atom to hold the final app.
no problem, it's good that we find those cases sooner than later
what's the best way to load a list of items and have them appended (rather than replace) to list at the :target site?
@roklenarcic: IIRC there's an option :append
, also :prepend
. It should be described in the source code documentation.
I think in your case you wouldn't use :target
but go straight for a post mutation and use integrate-ident!
there.
sadly I don't get the entity in post-mutation, I'll need to reach into the state and pull it out, worse yet, I'll need to temporarily store the result somewhere to append it later
yeah I tried to do it myself, but after the load, the browser enters an infinite loop when trying to denormalize
I push the previous list of idents into the load, and then I return that same list with an extra item at first position, which is not an ident, and the browser dies
I can't solve a simple scenario where I need to load an entity and add it to a list
without browser entering into an infinite loop
@roklenarcic You are just dealing with data structures, keeping the state data structure normalized (with idents). So if it helped you could try out your mutation in Clojure. If you app state is small you could post it up somewhere and we could check if indeed your state is normalized.
Well here it is: https://pastebin.com/u5FDP2Ri
I'll need to just turn the whole thing upside down and do it somehow differently
The :params
should be simple bits of data that are in your UI. One param that :search/med-parent
takes is :parent-list
. You have it as something that is actually accessing your state. Position the call to load
in a defui
where that state already is.
hm let me present a very simple contrived example
Even if you stick with the way you are doing it can you determine the value of (get-in @(om/app-state (:reconciler app)) [:medlist-by-level "vtm"])
before doing the load? Also you could try not grabbing that value but instead sending across a hard coded value.
imagine you have a list and a search box, every time a user hits enter, you run a load, which returns a list of data, denormalized of course. But instead of showing this list as is, you concat the new results to the ones you are already showing in the listbox (which contains idents)
how would you make that happen?
I would be accumulating the data in client state. The defui would refer to it - react would be making that happen automatically for me.
how would you accumulate that? Currently the load replaces an element in state. My solution was to make load target the list itself, then pass the old list as parameter to load and have the load function return the concatenated list, which of course then contains a mix of idents (from old list) and new denormalized data. And it seems that it makes the browser hang.
I'm not sending the old list to the server
which mutation?
well a load changes client state for you, but it will be doing a mutation under the covers.
isn't the 'view' the search box fills up a piece of state? so you would have it in your app state, and then do a (swap! state update :ui/search-results #(into % results))
, right?
I mean yeah, that's one solution
to load into a hidden prop
and then move it
btw load-field-action disallows params that are not the field key
like if I add :params {:sorted true} to load-field-action call it errors out with [ 8.302s] [om.next] Error: You attempted to add parameters for #{:sorted} to top-level key(s) of [{:parent ...}]
is there some kind of command I need to add to make load-field-action calls go through? Network implementation never gets called, I have 50 items in :fulcro/ready-to-load, and 0 items in :fulcro/loads-in-progress
@roklenarcic just built something like that. Search, debounce on 3 chars, with loading screen for enter and no loading + append for load more. I can share the code when I get home (at work now). 🙂
you loaded into a temporary key and then moved it in another mutation?
I'm not sure if it's just me or not, but the first sentence from devcards/fulcro_devguide.E_UI_Queries_and_State
is making very little sense to me.
>Now that you understand the database format, how you get data out of that database via the queries, and how you build parser code once you're ready to get some UI on the screen via all of those things?
Now that you understand three things: 1./ the database format, 2./ how you get data out of that database via the queries, and 3./ how you build parser code You're now ready to get some UI on the screen. How do you do that (via those 3 things)?
It's like reading a crazy Haskell one-liner after a long while of lisping; it seems like it should make sense to someone, but for me it's more akin to a simulated stroke.
@roklenarcic your stack trace has nothing to do with you networking call. That stack trace is for pulling data for rendering. What you've shown for state initially looks fine as far as I can see, but it does seem like you've potentially done something to state that exposed a bug in Om's db->tree. Given how many people use that, I would be really surprised if your app State actually looked sane after your load. It seems like kind of a bad idea that you're both trying to simulate a whole different style of networking while learning the basics. I think you're causing yourself a lot more hassle than you need to. why don't you learn the basics, then worry about hooking up to an alternate form of networking?
@driphter The docs get heavily edited over time. It's obvious that sometimes mistakes get made and confusing sentences result. I probably accidentally joined two sentences together and didn't read the second one to see if it still made sense.
@tony.kay what do you think is the best way to run my webserver with a single command (for testing)
I basically need to do the equivalent of env JVM_OPTS=-Ddev lein repl
and go
but in one command
ideally something that i can alias with and chain together with lein do start-webserver, compile-devcards, run-visual-regression-specs
that’s basically what i had with boot
So far I got this
:aliases {"start-webserver" ["exec" "-ep" "(use 'arc.server-main) (-main)"]}
but I’m not sure if that’s an abuse?Seems to work though 😅
@currentoor why not make an uberjar and just run it?
is there a way to build uberjar and run with just one command? and can it be chained together with other “tasks”
or lein run -m clojure.main -e “(require ‘fulcro-devguide.upload-server)” -e “(fulcro-devguide.upload-server/go)”
what’s the upload server?
oh i see
and is there a way to make it exit?
after i’ve done my visual testing?
lol seriously?
i guess i could just have an exit alias pointing to "exit" ["exec" "-ep" "(System/exit)"]
and put that at the end of the list of commands to lein do
it would feel strange writing an endpoint in my webserver that causes it to kill itself 😅
i could probably put safe guards so it doesn’t happen in prod but still
well, you have to have some communication for that. Either kill it from outside with a script, or put something inside
You could easily make the namespace look for some JVM option that enables it, so it won’t be there in production
(defmutation die ...
(when (System/getProperty "enableExitMutation") (System/exit 0)))
or realize that it is a multimethod and don’t even run the defmethod if the property isn’t defined:
(when (System/getProperty "...")
(defmutation ...))
i’ll try
well our “html” looks like this
(defn cards [req]
(layout
[:title "Report Components"]
[:body {:style {:background-color "#fff"}}
[:script (str "window.build = \"cards\";")]
[:script {:src "js/cards.js" :type "text/javascript"}]
[:script {:type "text/javascript"} "window.DEVCARDS = true;"]]
:tracking? false))
looks like you were right about (System/exit 0)
not killing the webserver
You could throw together a new namespace with a separate self-killing server and a main. Just have it time out after a minute or so
yeah that sounds simpler
that way you leverage all the same server code, but when you run it from that entry point you don’t have to worry about shutting it down…it’ll just go away after perhaps a period of inactivity?
this was the sort of thing that boot excelled at
throw in a bit of middleware at the front of the stack that resets a timer every time it sees traffic
right, cause you were writing the build system within the same JVM…it wasn’t external executions
with boot i could compose tasks together in the same jvm
right
so unrelated question
any other languages peak your interest?
like if clojure didn’t exist, what do you think would be your pick?
good idea