A router for browsers that supports asynchronous routing logic. Bring your own printing and parsing (check out routing-duplex).
For a basic React example see here.
$ spago install web-router
data Route = Page Page | NotFound data Page = Home | ProductList | ProductView ProductId | About | ContactUs type ProductId = Int
This example uses routing-duplex.
Imports
import Prelude hiding ((/)) import Data.Either (Either) import Data.Generic.Rep (class Generic) import Routing.Duplex (RouteDuplex', default, end, int, parse, print, root, segment) import Routing.Duplex.Generic (noArgs, sum) import Routing.Duplex.Generic.Syntax ((/)) import Routing.Duplex.Parser (RouteError)
derive instance Generic Route _ derive instance Generic Page _ productId :: RouteDuplex' ProductId productId = int segment routes :: RouteDuplex' Route routes = default NotFound $ sum { "Page": pages , "NotFound": "404" / noArgs } pages :: RouteDuplex' Page pages = root $ end $ sum { "Home": noArgs , "ProductList": "products" / noArgs , "ProductView": "products" / productId , "About": "about" / noArgs , "ContactUs": "about" / noArgs } -- | This is the route parser we need to pass to the driver. -- | It can produce any route which allows the parser to return a value of `NotFound` instead of failing. parseRoute :: forall String -> Either RouteError Route parseRoute = parse routes -- | This is the route printer we need to pass to the driver. -- | It can only print paths to valid pages, which means a path can't be produced for the `NotFound` route. -- | With this approach routes can be seperated based on whether they should be a navigation target and have a URL. -- | Note: assymetry is not required, and a symmetrical printer works as well. printRoute :: Page -> String printRoute = print pages
Imports
import Web.Router as Router
onNavigation :: Maybe Route -> Route -> Router.RouterM Route Page Router.Routing Router.Resolved Unit onNavigation previousRoute requestedRoute = case requestedRoute of NotFound -> case previousRoute of Just (Page page) -> Router.do liftEffect showBrokenNavigationMessage Router.redirect page -- redirect back to the previous page and show a message _ -> Router.continue -- no previous page, so just show the "not found" page _ -> Router.do access <- liftAff fetchUserAccess if userHasAccess requestedRoute access then Router.continue -- they have access, so resolve with the requested page else Router.override NotFound -- no access, so pretend the page doesn't exist onEvent :: Router.RoutingEvent Route -> Effect Unit onEvent newEvent = case newEvent of Router.Routing previousRoute requestedRoute -> showNavigationSpinner Router.Resolved previousRoute newRoute -> hideNavigationSpinner setCurrentRoute newRoute
Imports
import Web.Router as Router import Web.Router.PushState as PushState
mkRouter :: Effect (Router.Router Route Page) mkRouter = do driver <- PushState.mkInterface parseRoute printRoute router <- Router.mkInterface onNavigation onEvent driver pure router
Both pushstate and hash drivers are included, or a custom driver can be implemented. An example of a custom driver could be one that synchronises some navigation state over sockets, for an experience where one user's behaviour could be broadcast to multiple users to follow along.