Fork me on GitHub
#re-frame
<
2017-10-26
>
mhuebert08:10:36

is the subviz tab in re-frame-trace supposed to work?

danielcompton19:10:59

Nope, I've meant to remove it for a while.

mhuebert08:10:14

it doesn’t seem to for me, and its d3 dependency adds some complexity and size to my build

mhuebert08:10:32

I know it didn’t work ~x months ago but notice the tab hasn’t been removed

borkdude09:10:58

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

jfntn13:10:02

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

kasuko14:10:03

@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.

kasuko15:10:34

@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

borkdude15:10:56

Yes, that’s the issue

borkdude15:10:14

I also tried other approaches, they didn’t work when the api key changed

kasuko16:10:22

I’ve hacked it in a couple other ways. Might I ask what you dislike about the r/track!?

kasuko16:10:19

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.

kasuko16:10:00

However, you are adding an additional constraint that you only want the reactive chain (1) to happen when (2) is happening.

kasuko16:10:17

This is exactly what your example is doing.

kasuko16:10:56

I’d only change your :organisation-users sub to dispose the fetch-users track

kasuko16:10:47

(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))))))

borkdude17:10:21

@U0YKGA0BT I updated the SO answer with your fragment

kasuko18:10:08

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 😛

borkdude09:10:18

It’s an interesting case for sure.

genRaiy09:10:46

this might be late but what about the mother of all async flow? https://github.com/Day8/re-frame-async-flow-fx

genRaiy09:10:46

I just used this to clean up an authentication flow and it is a thing of beauty

iGEL14:10:51

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 subscription

kasuko14:10:00

@igel that is correct

iGEL14:10:07

Thanks 🙂

mikerod15:10:55

An 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.

kah0ona17:10:45

yeah i’ve responded to your question before, but I agree. I had the same experience.

kah0ona17:10:04

@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.

kah0ona17:10:33

I read indeed that the docs now suggest moving this to subscriptions which sound logical, but haven’t gotten round to try it

kah0ona17:10:50

but it seems that it’s not without hoops to jump through then? 🙂

kah0ona17:10:31

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.

kah0ona17:10:58

it’d just be nice if you could put in a subscription and be done with it, as the docs indeed suggest

kah0ona17:10:17

I realize that i’m not of any help; just telling my experience 🙂

borkdude17:10:45

@kah0ona Usually I also do this, and dispatch it from a component, but the docs recommend against this.

kah0ona17:10:51

yeah and i’m still in doubt if it’s as bad as the docs state

kah0ona17:10:58

because if it’s not put there, where to put it? it has to go somewhere

kah0ona17:10:20

of course, changing to a subscription based setup like advised, this problem goes away completely

borkdude17:10:18

@kah0ona Yes, this one is easy, but the question on Stackoverflow was more complex

borkdude17:10:00

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

ag18:10:57

any thoughts?

borkdude18:10:01

@ag What do you mean ‘recognizes as a download’?

yury.solovyov18:10:39

maybe content-disposition: attachment header or something like that?

ag18:10:23

@yury.solovyov I think I've tried different ways of setting :response-format and :headers - it still didn't work.

yury.solovyov18:10:51

was a guess, nothing more

ag18:10:27

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")

yury.solovyov18:10:37

or an Iframe that has src == href ?

ag18:10:04

@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

borkdude18:10:41

@ag In the answer they suggest you have a form in your html which you submit through js

borkdude18:10:51

and this will present the download

ag18:10:26

the trick is - to make it work from within re-frame's handler. I wonder if I can use goog.net.FileDownloader

borkdude18:10:27

@ag Just use a side effect as part of reg-event-fx ?

ag18:10:48

> Just use a side effect as part of reg-event-fx ? not sure what you mean

borkdude18:10:24

I mean, don’t use http-fx, it probably won’t get you there

borkdude18:10:36

you can make an event e.g. :download-thing: (reg-event-fx :download-thing (fn [{:keys [:db]}] [_]] (click-my-link!))

borkdude18:10:53

where click-my-link clicks your link with a :download attribute

borkdude18:10:59

and of course you can hide this link using css

ag18:10:45

reg-event-fx still has to return a map, no?

borkdude18:10:17

it can also return nil, so e.g. (reg-event-fx .... (side-effect!) nil), then it won’t affect your db

borkdude18:10:59

or return {:db db}, same thing

ag18:10:19

kk, let me try

mikerod18:10:08

@ag @borkdude isn’t this what you use reg-fx for?

borkdude18:10:59

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

mikerod18:10:26

No, I just mean for the side-effect of initiating the download

borkdude18:10:32

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.

ag18:10:58

damn it... I can't just "click a button" it has to go with the auth-header and shit

mikerod18:10:30

(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
   ))

borkdude18:10:40

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”})

mikerod18:10:24

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

mikerod18:10:35

it also has a cljsjs entry for closure compiler, but only needs like 1 extern

mikerod18:10:24

(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

mikerod18:10:44

@borkdude yeah, I think it is probably easy to overlook reg-fx

ag18:10:17

@mikerod do you think it's possible to just use goog.net.FileDownloader? why add another dependency?

mikerod18:10:18

FileSaver.js is nice, but you can do it yourself if you want to deal with IE & Safari quirks

mikerod18:10:30

I haven’t looked much at it. Will check it out

borkdude18:10:36

I’m still thinking in ‘must support IE9’ mode 😉

ag18:10:43

Thank god, I don't have to deal with that kind of shit

mikerod18:10:39

maybe I just didn’t know

ag18:10:32

alright guys. Thanks for the help. I'm gonna try js/saveAs thing I guess

borkdude18:10:09

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

mikerod18:10:08

Interesting

mikerod18:10:34

It isn’t immediately clear to me what compatibility goog.net.FileDownloader has with browsers

mikerod18:10:46

didn’t see it documented, read through the code briefly and was not enlightened

mikerod18:10:52

that’s my super mini research on the topic

mikerod18:10:12

I know FileSaver.js is super popular at least 😛

mikerod18:10:47

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.

mikerod18:10:29

Looks like it is IE10+ though, so if you’re in @borkdude’s world of IE9+, god speed 😛

borkdude18:10:44

I’m not anymore in the situation, luckily 🙂

borkdude18:10:23

but the link with a download attribute sounds simple enough to me… no js required, except for clicking on it

borkdude18:10:41

we use it on our app too, to download a pdf

ag18:10:24

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?

borkdude18:10:31

I think so yes

borkdude18:10:03

it’s not you 😉

borkdude19:10:00

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”}]])})))

mikerod19:10:46

> 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

mikerod19:10:55

FileSaver.js uses the download attribute on the happy path I believe

mikerod19:10:03

It’s been a while since I looked at this though

borkdude19:10:36

I think we’re good, we don’t support IE

ag19:10:57

guys, compatibility is not an issue for me. I just need to make sure it works in Chrome

mikerod19:10:04

I had to support IE11+

borkdude19:10:32

@ag then you’re fine with the way I showed the code off

borkdude19:10:48

assuming you’re using reagent or something similar, but even with jquery you can make this work

borkdude19:10:08

o wait, we’re in the re-frame channel here 😉

mikerod19:10:20

Yeah, your options are more open if you don’t have to deal with crazy browsers.

borkdude19:10:25

completely lost track of that detail 😉

ag19:10:08

oh, well.. again - I can't just simulate clicking a button - I have to send a request with the auth-header and such.

borkdude19:10:32

I have to go. Good luck @ag!

ag19:10:32

Thanks!