This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-10-26
Channels
- # aws (1)
- # aws-lambda (16)
- # beginners (8)
- # boot (2)
- # cider (4)
- # cljsrn (9)
- # clojure (137)
- # clojure-italy (3)
- # clojure-russia (39)
- # clojure-spec (34)
- # clojure-uk (33)
- # clojurescript (44)
- # core-logic (11)
- # cursive (27)
- # data-science (16)
- # datomic (52)
- # duct (1)
- # emacs (1)
- # figwheel (2)
- # fulcro (90)
- # graphql (3)
- # hoplon (7)
- # lambdaisland (2)
- # leiningen (23)
- # lumo (1)
- # off-topic (1)
- # om (40)
- # onyx (44)
- # re-frame (116)
- # reagent (3)
- # shadow-cljs (87)
Nope, I've meant to remove it for a while.
it doesn’t seem to for me, and its d3 dependency adds some complexity and size to my build
Still looking for improvement on the only answer here, since I’m not very satisfied with it: https://stackoverflow.com/questions/46895879/re-run-subscription-on-input-change
The issue in the original question is due to the form 2 component and the subscription for the api token being made outside of the render fn. it would update as expected if changed to a form 1
@U0CJ8PTE1 I don’t think it would. The issue is that the fetch is outside the reaction. So while changing it to a form-1 would re-run the subscription when a new api-key arrives, it will only re-run the reaction which does not include the fetch.
@borkdude Is the issue just the presence of r/track!
? You could potentially achieve the same thing by placing this in a subscription chain. With the fetch existing within the reaction. Let me try to mock something out
I’ve hacked it in a couple other ways. Might I ask what you dislike about the r/track!
?
The more I work on this the more I see that you need 2 separate reactive chains. 1. on api-token
token change, fetch-data and 2. on new-data update UI. Now the problem is that the fetch-data can not be tied to an app-db reaction or you are going to fire it a ton of times.
However, you are adding an additional constraint that you only want the reactive chain (1) to happen when (2) is happening.
(defn fetch-users! []
(when-let [api-token @(subscribe [:api-token])]
(re-frame/dispatch [:fetch-organisation-users api-token])))
(re-frame/reg-sub-raw
:organisation-users
(fn [app-db [_]]
(let [fetcher (r/track! fetch-users!)]
(reagent.ratom/make-reaction
(fn []
(get @app-db :organisation-users)
:on-dispose #(dispose! fetcher))))))
@U0YKGA0BT I updated the SO answer with your fragment
Ya other than that, I can’t think of a cleaner way to do it, I tried wrapping it in a reaction, I tried having a side-effect subscription that was dependant on the api-key and both involved more linked code than the track. I’m not saying it can’t be done, just that I am tossing in the hat 😛
this might be late but what about the mother of all async flow? https://github.com/Day8/re-frame-async-flow-fx
Hey! Do I get it correctly, that if I have a subscription that depends on other subscriptions like this
(reg-sub
:my-subscription
:<- [:other-subscription]
; ...
Then the result of my-subscription
will be cached after the first call until other-subscription
changes? I want to do quite some costly computations in that subscriptionAn interesting pattern emerged for me as I’ve been experimenting rewriting parts of my app with re-frame
instead of reagent
.
In my former approach, when there was an event that triggered perhaps some new data being loaded, I’d import that data into the single app state atom I had. However, I’d also end up needing to clear or reset a bunch of other parts of the app-state, explicitly, that were dependent on that. This was a consequence of how I was modeling parts of the app state in a way that were tailored to the access patterns of pieces of the state.
re-frame
introduced me more to the reaction
(via subscriptions) way of thinking about this and it has been pretty nice so far. Now data is loaded into the app state (via events), but I don’t have bother explicitly “clearing” or reseting dependent state in the app state because I don’t have any concrete state to reset. Instead these specific access patterns bits from before are done via materialized views (layer 3 subscriptions). The materialized views already capture this “state dependent” information and manage it automatically.
I’m saying this in part to just explain my experience right now, but also I’m curious if others have found similar results. I have asked before a somewhat related question, but I’m just finding more and more use from the “materialized view” idea managed by subscriptions. It seems to have helped with the problem I had of a lot of ui-widget sort of state being based on some loaded data state.
@borkdude up until now i’ve always used events to fetch the data, then store it when it returns, and then make other subscriptions depend on it.
I read indeed that the docs now suggest moving this to subscriptions which sound logical, but haven’t gotten round to try it
the most annoying thing is, doing it the event way, is that you somewhere have to call some init dispatch that kicks off the downloading.
it’d just be nice if you could put in a subscription and be done with it, as the docs indeed suggest
@kah0ona Usually I also do this, and dispatch it from a component, but the docs recommend against this.
of course, changing to a subscription based setup like advised, this problem goes away completely
which would also be easier if you’d be allowed to dispatch from a component, which we do all the time. Although I would wrap the dispatch in a when
to prevent unnecessary ones
maybe content-disposition: attachment
header or something like that?
@yury.solovyov I think I've tried different ways of setting :response-format
and :headers
- it still didn't work.
was a guess, nothing more
I wonder if it works if I do the stupidest thing: - right in the handler I put: (js/window.open (str "/download-csv?" (url/map->query params)) "_blank")
or an Iframe that has src == href ?
@borkdude I too, first thought that it's because it was post
.. and I've changed it to get
, but that didn't work either
@ag In the answer they suggest you have a form in your html which you submit through js
the trick is - to make it work from within re-frame's handler. I wonder if I can use goog.net.FileDownloader
I read here you can also use an :a
tag with :download
attribute
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#Using_the_download_attribute_to_save_a_<canvas>_as_a_PNG
you can make an event e.g. :download-thing
:
(reg-event-fx :download-thing (fn [{:keys [:db]}] [_]] (click-my-link!))
it can also return nil, so e.g.
(reg-event-fx .... (side-effect!) nil)
, then it won’t affect your db
Only if you want to make it more general. E.g. if you want to write your own http-fx
:
https://github.com/Day8/re-frame-http-fx/blob/master/src/day8/re_frame/http_fx.cljs#L89
See https://github.com/Day8/re-frame/blob/master/docs/Effects.md#effects-with-no-data
Oh you can do that. Maybe I’m abusing reg-event-fx
for it, but you can do the side effect from multiple places, even from reg-event-db
etc.
(reg-event-fx
:xhr-success-happened
(fn [cofx [_ some-data-retrieved]]
{:do-download-it some-data-retrieved}))
(reg-fx
:do-download-it
(fn [some-data-retrieved]
;; Logic on how to download here
))
But I still think that example illustrates how you could make it more general. Like:
(reg-fx :download (fn [download-link] (click-my-link! download-link)))
and then
(reg-event-fx :download-it (fn [coeffect _ ] {:download “/foo.pdf”})
As far as downloading from xhr response like that, I’ve just used the JS Blob API in combination with FileSaver.js since has pretty good browser compatibility
(reg-fx
:do-download-it
(fn [some-data-retrieved]
(let [blob (js/Blob. #js[some-data-retrieved] #js{"type" "text/csv;charset=UTF-8"})]
(js/saveAs blob filename))))
assuming you are dealing with csv dat ahere@mikerod do you think it's possible to just use goog.net.FileDownloader
? why add another dependency?
FileSaver.js is nice, but you can do it yourself if you want to deal with IE & Safari quirks
Closure also has a very nice iframeIo package, which I’ve used to support uploading pictures via javascript once, to support IE9: https://google.github.io/closure-library/api/goog.net.IframeIo.html
It isn’t immediately clear to me what compatibility goog.net.FileDownloader
has with browsers
and before I used it, I implemented a few different browser stuff by hand and it didn’t make sense once I saw that FileSaver.js was doing the same thing, but better and dealing with more edge cases.
Looks like it is IE10+ though, so if you’re in @borkdude’s world of IE9+, god speed 😛
but the link with a download attribute sounds simple enough to me… no js required, except for clicking on it
in general: it's not re-frame specific problem, right? For whatever reason (security whatnot) one cannot simply force "file download" using ajax request. Is that correct?
here is how we do it:
(defn preview-instructions [query]
(let [!click (atom nil)
loading? (subscribe [::subs/report-loading?])
ready? (subscribe [::subs/report-ready?])]
(r/create-class
{:component-did-update
(fn []
(when @ready?
(ocall @!click “click”)))
:reagent-render
(fn []
[:div
{:style {:position :absolute
:right 0}}
[ladda “Generate Report”
{:loading @loading?}
{:data-size “s”
:data-style “slide-up”
:on-click #(dispatch [::events/do-report])
:class “ladda-button btn btn-primary”}]
[:a
{:style {:visibility :hidden
:width “1px”
:height “1px”}
:ref (fn [com] (reset! !click com))
:title “Download PDF”
:download (str now “_” “report.pdf”)
:target “_self”
:href “/report”}]])})))
> but the link with a download attribute sounds simple enough to me… no js required, except for clicking on it I think the main issue with that is browser compatibility
assuming you’re using reagent or something similar, but even with jquery you can make this work