Zaid-Ajaj / Feliz.Router

A router component for React and Elmish that is focused, powerful and extremely easy to use.
MIT License
78 stars 16 forks source link

Start Routing from a base path #38

Closed SCullman closed 3 years ago

SCullman commented 3 years ago

While working for a starter kit to use Feliz within a CMS, I created an extension Feliz.Router.BasePath to enable routing from URLs that have different base URLs than just '/ ' but should still offer pathMode.

The extension offers three new hooks:

type BaseUrl =
    { /// Current Url without basePath
      current: string list
      /// Navigates to the provided url
      /// * param url
      goto: string list -> Browser.Types.MouseEvent -> unit
      /// Creates a path including the base path
      /// * param url
      href: string list -> string }

router.useBaseUri (): (BaseUrl * string list-> unit)
router.useBasePath (path: string) : (BaseUrl * string list-> unit)
router.useBaseUrl (url: string list): (BaseUrl * string list-> unit)

Example:

feliz.Router
Feliz.Router.BasePath

[<ReactComponent>]
let App () =
    let (url, urlChanged) = router.useBasePath ("/some/where/deeply/nested")

    let activePage =
        match url.current with
        | [ ] -> Html.h1 "Home"
        | [ "users" ] -> Html.h1 "Users page"
        | [ "users"; Route.Int userId ] -> 
           Html.h1 (sprintf "User ID %d" userId)
        | _ -> Html.h1 "Not found"

    React.router [ router.pathMode
                   router.onUrlChanged (urlChanged)
                   router.children [ activePage ]

I think there are other use cases that might be interesting. E.g. router.useBaseUrl would also allow routing in sub-components / sub-pages within a normal Feliz application.

Therefore it might be better to either

What do you think?

Zaid-Ajaj commented 3 years ago

Hi @SCullman this looks very interesting. The easier option is to publish a separate package though I would love to add it as part of Feliz.Router so people don't to look for many packages. The thing is, there is a PR open at #30 which implements this functionality but depends on the base element on the page which I believe is the "standard" way to do it

<base href="/some/where/deeply/nested" />

where the API of Feliz.Router is pretty much kept the same but it takes this base element into account. I think this would also solve your deeply nested URL problem, right? What do you think about this solution?

SCullman commented 3 years ago

Hi @Zaid-Ajaj, I missed PR #30, otherwise, I might not have started an own attempt.

If the document includes a base tag like <base href="/some/where/deeply/nested" />, it changes the baseUri of the document. Then the hook router.useBaseUri() comes into play and queries that value and use it instead of a given, manual path. In fact, that hook is the most important one and the one I am using on my own.

No matter which hook is used, the URLs / string lists would always be the same as in hashMode.

Feliz.Router.BasePath requires zero changes to the source of Feliz.Router. Therefore, it could easily be a separate package.

SCullman commented 3 years ago

I decided to move the code to its own repository and created a separate package. IMHO it feels more like an application of Feliz.Router than a part of it.

I also checked #30. It looks very similar but detects the basepath directly from the base tag instead of document.baseUri. Both Base.href and document.baseUri might contain a full path instead of a relative path, but I think - as the URL is still just a string - it should still work. But maybe formatPath will return some rubbish.

Anyway, I am aware of situations where there is no base tag at all and you don't control the HTML of the containing page. Then router.useBasePath can be very handy.