Skip to content

Routing

Loic Denuziere edited this page Nov 8, 2018 · 9 revisions

MiniBlazor provides facilities to bind the page's URL to the Elmish model.

Inferred router

The easiest way to create a router is by using an inferred router. In this mode of operation, you create an endpoint type which has a 1-to-1 correspondance with your supported URLs, and store it in the Elmish model.

Here are the steps to set up an inferred router:

  1. Create the endpoint type, which must be an F# union type. Each case corresponds to a path prefixed by the case's name. The case's arguments are the consecutive fragments of the path. For example:

    type Page =
        | Home                                  // -> /Home
        | BlogArticle of id: int                // -> /BlogEntry/42
        | BlogList of user: string * page: int  // -> /BlogList/tarmil/1

    To customize the prefix fragment, you can use the EndPoint attribute:

    type Page =
        | [<EndPoint "/">]
          Home                                  // -> /
        | [<EndPoint "/article">]
          BlogArticle of id: int                // -> /article/42
        | [<EndPoint "/list">]
          BlogList of user: string * page: int  // -> /list/tarmil/1

    Arguments can have one of the following types:

    • string
    • bool
    • integer: int8 (aka sbyte), uint8 (aka byte), int16, uint16, int32 (aka int), uint32, int64, uint64
    • decimal
    • float: single, double (aka float)
  2. Add a field in the Elmish model that stores the endpoint:

    type Model =
        {
            page: Page
            // other fields...
        }
  3. Add an Elmish message that sets the endpoint:

    type Message =
        | SetPage of Page
        // other messages...
    
    let update message model =
        match message with
        | SetPage page -> { model with page = page }
        // other messages...
  4. Create the router with Router.infer:

    let router = Router.infer SetPage (fun m -> m.page)
  5. Attach the router to the Elmish program:

    Program.mkSimple initModel update view
    |> Program.withRouter router

The router has a few helpful utilities:

  • router.Link takes an endpoint value and returns the corresponding URL.

    navLink NavLinkMatch.All (router.Link Home) [] [text "Go to Home"]
  • router.HRef takes an endpoint and returns an href attribute pointing to the corresponding URL.

    a [router.HRef Home] [text "Go to Home"]

Custom router

To have more control over the exact shape of your URLs, you can create a custom router like follows.

let customRouter : Router<Page, Model, Message> =
    {
        // getEndPoint : Model -> Page
        getEndPoint = fun m -> m.page
        // setRoute : string -> option<Message>
        setRoute = fun path ->
            match path.Trim('/').Split('/') with
            | [||] -> Some Home
            | [|"article"; id|] -> Some (BlogArticle (int id))
            | [|"list"; user; page|] -> Some (BlogList (user, int page))
            | _ -> None
            |> Option.map SetPage
        // getRoute : Page -> string
        getRoute = function
            | Home -> "/"
            | BlogArticle(id) -> sprintf "/article/%i" id
            | BlogList(user, page) -> sprintf "/list/%s/%i" user page
    }

Note: if the URL is changed in such a way that setRoute returns None, then the model is not updated.

You can also create a router that maps directly to the model without an intermediary endpoint type. However this router type doesn't have some utilities such as Link and HRef.

let customRouter2 : Router<Model, Message> =
    {
        // setRoute : string -> option<Message>
        setRoute = fun path ->
            match path.Trim('/').Split('/') with
            | [||] -> Some Home
            | [|"article"; id|] -> Some (BlogArticle (int id))
            | [|"list"; user; page|] -> Some (BlogList (user, int page))
            | _ -> None
            |> Option.map SetPage
        // getRoute : Model -> string
        getRoute = fun model ->
            match model.page with
            | Home -> "/"
            | BlogArticle(id) -> sprintf "/article/%i" id
            | BlogList(user, page) -> sprintf "/list/%s/%i" user page
    }
Clone this wiki locally