Working with reflex-dom: generic event stream for route changes

Sometimes when I face a more interesting challenge when working with obsidiansystems obelisk, I open an issue there. Usually the issue gets correctly categorized as “Question” and thus shouldn’t clutter their bug tracker.

However, I am missing a forum to discuss question, design decisions and the general topic of learning reflex-dom.
I usually don’t know in how far my question is related to either reflex-platform, reflex-dom or specifically to obelisk.
I think this forum is a good place for asking my questions w/o opening issues in any of those repositories every time.
… if anyone here is interested in discussing reflex-dom, of course :slight_smile:

This is my question:

copied from get an event that fire everytime the route changes, generically · Issue #1028 · obsidiansystems/obelisk · GitHub

In short, I want to react to route changes like this:

setRoute evNewRoute
tellEvent $ evNewRoute $> someImportantData

only that i want to tell someImportantData at any setRoute and even when the user just clicks a hyperlink.


The long version, in order to avoid the XY problem:

I decided to reduce all loading widgets of my somewhat complex website to just one:
a spinner right on top of the page that disappears when the page has loaded.

I implement this global “loading state widget” by having the following datatype in my application state:

data Loading = LoadingStill | LoadingDone | LoadingError Text

data AppState = Appstate
  { stLoading :: Loading
  }

My pages implement EventWriter t (AppState -> AppState) and can set the loading state to LoadingStill when initiating a web request and to LoadingDone or LoadingError when the response comes in.
So far so good.

However, one of my pages has a heavy loading time in the absence of any web request.
No problem: just use the post-build event to set the loading state to LoadingDone.
But there comes the issue: any hyperlink on the website next to any setRoute has to do tellEvent $ evRouteChange $> stLoading .~ LoadingStill.

Is is possible to get access to some generic event stream that fires on any route change, at all?


Btw, having a dedicated space (or tag) inside discourse.haskell.org just for reflex-dom would be interesting, too.
I would use it to publish and discuss recipes that I use on my website.
And as far as I know, a lack of examples/tutorials/recipes and general documentation is still a major hurdle to learning reflex-dom.

5 Likes

Now having read the implementation of routeLink in Obelisk.Route.Frontend, I realize that such a generic event stream isn’t possible:

  enc <- askRouteToUrl
  let cfg = (def :: ElementConfig EventResult t (DomBuilderSpace m))
        & elementConfig_eventSpec %~ addEventSpecFlags (Proxy :: Proxy (DomBuilderSpace m)) Click (\_ -> preventDefault)
        & elementConfig_initialAttributes .~ "href" =: enc r
  (e, a) <- element "a" cfg w
  setRoute $ r <$ domEvent Click e

routeLink builds a HTML-element that doesn’t forward the click and then puts in setRoute to make sure the click is processed by reflex-dom's event model instead.

I can replace setRoute with my custom version everywhere (including a custom version of routeLink) and have my custom version do tellEvent, as I need.


For links inside HTML that is generated from markdown I don’t have any great options.
If I want, I can change the way the HTML is generated and give the hyperlinks an extratreatment.

Alternatives:

I can use javascript to react to changes of the url. But from within javascript, I would need to call a haskell function (tellEvent) and I would have to maintain that javascript event handling next to reflex-dom.

1 Like