ajnsit / concur

An unusual Web UI Framework for Haskell
https://ajnsit.github.io/concur/
BSD 3-Clause "New" or "Revised" License
305 stars 21 forks source link

Routing #5

Open mpdairy opened 7 years ago

mpdairy commented 7 years ago

Have you thought about a clever way to do routing with Concur? I'm planning on using something like servant-router to parse a Servant API in the client and then load the correct corresponding widget, and something like ghcjs-servant-client to generate functions to jump to the route (maybe just by doing a JS History pushState).

Then I think I'd just need a loop at the bottom that listened for changes to the location/history state and loaded the new widget / killing the old one.

But the big downside of this is that whenever I want to route a change I can't use the nice monad flow to do menus like in your example, plus it seems pretty bad to be able to jump to any other route within any widget; it could turn to spaghetti real fast.

ajnsit commented 7 years ago

Well I haven't implemented any routing apps yet. At the most basic level I can think of the following scheme. However it would be good to brainstorm on an ideal API.

So let's say we have data Route = A | B | C. And we want to show widgetA, widgetB, widgetC respectively. Then it could look like this -

getRoute = <IO CODE TO FETCH AND PARSE CURRENT URL>
main = do
  route <- getRoute
  case route of
    A -> widgetA
    B -> widgetB
    C -> widgetC

That would handle the routing only once in the beginning. So to handle routing changes in real time we can put in a short circuit operation at the top level -

getRoute = <IO CODE TO FETCH AND PARSE CURRENT URL>
awaitRouteChange = <IO CODE TO AWAIT A PAGE CHANGE>
displayCurrentRoute = do
  route <- getRoute
  case route of
    A -> widgetA
    B -> widgetB
    C -> widgetC

main = forever $ displayCurrentRoute <|> awaitRouteChange

This will abort the current widget when the route changes, and redraw the widget for the new route.

mpdairy commented 7 years ago

I ended up using the ghcjs-servant-router library (https://github.com/plow-technologies/servant-ghcjs-router), but I modified it so all the routes return a concur Widget and the initRouter function runs runWidgetInBody . forever on the widget returned by the route. With that I can do nested routes and wrap widgets around nested routes to provide menus or back buttons. If anybody wants me to make a nice fork of it with a demo, let me know.

ajnsit commented 7 years ago

@mpdairy that is awesome! An example demo would be great!