GithubHelp home page GithubHelp logo

zaid-ajaj / feliz.router Goto Github PK

View Code? Open in Web Editor NEW
77.0 5.0 16.0 537 KB

A router component for React and Elmish that is focused, powerful and extremely easy to use.

License: MIT License

F# 98.87% HTML 0.26% JavaScript 0.87%
fable fsharp elmish elm router routing navigation

feliz.router's Introduction

Feliz.Router Nuget Build status

A router component for React and Elmish that is focused, powerful and extremely easy to use.

Here is a full example in Feliz

module App

open Feliz
open Feliz.Router

[<ReactComponent>]
let Router() =
    let (currentUrl, updateUrl) = React.useState(Router.currentUrl())
    React.router [
        router.onUrlChanged updateUrl
        router.children [
            match currentUrl with
            | [ ] -> Html.h1 "Index"
            | [ "users" ] -> Html.h1 "Users page"
            | [ "users"; Route.Int userId ] -> Html.h1 (sprintf "User ID %d" userId)
            | otherwise -> Html.h1 "Not found"
        ]
    ]

open Browser.Dom

let root = ReactDOM.createRoot(document.getElementById "root")
root.render(Router())

Full Elmish example

module App

open Feliz
open Feliz.Router

type State = { CurrentUrl : string list }
type Msg = UrlChanged of string list

let init() = { CurrentUrl = Router.currentUrl() }
let update (UrlChanged segments) state = { state with CurrentUrl = segments }

let render state dispatch =
    React.router [
        router.onUrlChanged (UrlChanged >> dispatch)
        router.children [
            match state.CurrentUrl with
            | [ ] -> Html.h1 "Home"
            | [ "users" ] -> Html.h1 "Users page"
            | [ "users"; Route.Int userId ] -> Html.h1 (sprintf "User ID %d" userId)
            | _ -> Html.h1 "Not found"
        ]
    ]

Program.mkSimple init update render
|> Program.withReactSynchronous "root"
|> Program.run

Installation

dotnet add package Feliz.Router

The package includes a single React component called router that you can use at the very top level of your application

React.router [
    router.onUrlChanged (UrlChanged >> dispatch)
    router.children [
        Html.h1 "App"
    ]
]

Where it has two primary properties

  • router.onUrlChanged : string list -> unit gets triggered when the url changes where it gives you the url segments to work with.
  • router.children: ReactElement the element to be rendered as the single child of the router component, usually here is where your root application render function goes.
  • router.children: ReactElement list overload to be rendered as the children of the router
let currentPage = Html.h1 "App"

React.router [
    router.onUrlChanged (UrlChanged >> dispatch)
    router.children [
        Html.div [
            Html.h1 "Using the router"
            currentPage
        ]
    ]
]

router.onUrlChanged is everything

Routing in most applications revolves around having your application react to url changes, causing the current page to change and data to reload. Here is where router.onUrlChanged comes into play where it triggers when the url changes giving you the cleaned url segments as a list of strings. These are sample urls their corresposing url segments that get triggered as input of of onUrlChanged:

segment "#/" => [ ]
segment "#/home" => [ "home" ]
segment "#/home/settings" => [ "home"; "settings" ]
segment "#/users/1" => [ "users"; "1" ]
segment "#/users/1/details" => [ "users"; "1"; "details" ]

// with query string parameters
segment "#/users?id=1" => [ "users"; "?id=1" ]
segment "#/home/users?id=1" => [ "home"; "users"; "?id=1" ]
segment "#/users?id=1&format=json" => [ "users"; "?id=1&format=json" ]

Parsing URL segments into Page definitions

Instead of using overly complicated parser combinators to parse a simple structure such as URL segments, the Route module includes a handful of convenient active patterns to use against these segments:

type Page =
    | Home
    | Users
    | User of id:int
    | NotFound

// string list -> Page
let parseUrl = function
    // matches #/ or #
    | [ ] ->  Page.Home
    // matches #/users or #/users/ or #users
    | [ "users" ] -> Page.Users
    // matches #/users/{userId}
    | [ "users"; Route.Int userId ] -> Page.User userId
    // matches #/users?id={userId} where userId is an integer
    | [ "users"; Route.Query [ "id", Route.Int userId ] ] -> Page.User userId
    // matches everything else
    | _ -> NotFound

[<ReactComponent>]
static member Router() =
    let (pageUrl, updateUrl) = React.useState(parseUrl(Router.currentUrl()))
    let currentPage =
        match pageUrl with
        | Home -> Html.h1 "Home"
        | Users -> Html.h1 "Users page"
        | User userId -> Html.h1 (sprintf "User ID %d" userId)
        | NotFound -> Html.h1 "Not Found"

    React.router [
        router.onUrlChanged (parseUrl >> updateUrl)
        router.children currentPage
    ]

Of course, you can define your own patterns to match against the route segments, just remember that you are working against simple string.

Programmatic Navigation

Aside from listening to manual changes made to the URL by hand, the React.router element is able to listen to changes made programmatically from your code with Router.navigate(...). To use this function is as a command inside your update function the same functionality is exposed as Cmd.navigate.

The function Router.navigate (and Cmd.navigate) has the general syntax:

Router.navigate(segment1, segment2, ..., segmentN, [query string parameters], [historyMode])

Examples of the generated paths:

Router.navigate("users") => "#/users"
Router.navigate("users", "about") => "#/users/about"
Router.navigate("users", 1) => "#/users/1"
Router.navigate("users", 1, "details") => "#/users/1/details"

Examples of generated paths with query string parameters

Router.navigate("users", [ "id", 1 ]) => "#/user?id=1"
Router.navigate("users", [ "name", "john"; "married", "false" ]) => "#/users?name=john&married=false"
// paramters are encoded automatically
Router.navigate("search", [ "q", "whats up" ]) => @"#/search?q=whats%20up"
// Pushing a new history entry is the default bevahiour
Router.navigate("users", HistoryMode.PushState)
// to replace current history entry, use HistoryMode.ReplaceState
Router.navigate("users", HistoryMode.ReplaceState)

Generating links

In addition to Router.navigate(...) you can also use the Router.format(...) if you only need to generate the string that can be used to set the href property of a link.

The function Router.format has a similar general syntax as Router.navigate:

Router.format(segment1, segment2, ..., segmentN, [query string parameters])

Examples of the generated paths:

Router.format("users") => "#/users"
Router.format("users", "about") => "#/users/about"
Router.format("users", 1) => "#/users/1"
Router.format("users", 1, "details") => "#/users/1/details"

Examples of generated paths with query string parameters

Router.format("users", [ "id", 1 ]) => "#/user?id=1"
Router.format("users", [ "name", "john"; "married", "false" ]) => "#/users?name=john&married=false"
// paramters are encoded automatically
Router.format("search", [ "q", "whats up" ]) => @"#/search?q=whats%20up"

Example of usage:

Html.a [
    prop.href (Router.format("users", ["id", 10]))
    prop.text "Single User link"
]

Using Path routes without hash sign

The router by default prepends all generated routes with a hash sign (#), to omit the hash sign and use plain old paths, use router.pathMode

React.router [
    router.pathMode
    router.onUrlChanged (parseUrl >> PageChanged >> dispatch)
    router.children currentPage
]

Then refactor the application to use path-based functions rather than the default functions which are hash-based:

Hash-based Path-based
Router.currentUrl() Router.currentPath()
Router.format() Router.formatPath()
Router.navigate() Router.navigatePath()
Cmd.navigate() Cmd.navigatePath()

Using (anchor) Html.a tags using path mode can be problematic because they cause a full-refresh if they are not prefixed with the hash sign. Still, you can use them with path mode routing by overriding the default behavior using the prop.onClick event handler to dispatch a message which executes a Cmd.navigatePath command. It goes like this:

type Msg =
 | NavigateTo of string

let update msg state =
  match msg with
  | NavigateTo href -> state, Cmd.navigatePath(href)

let goToUrl (dispatch: Msg -> unit) (href: string) (e: MouseEvent) =
    // disable full page refresh
    e.preventDefault()
    // dispatch msg
    dispatch (NavigateTo href)

let render state dispatch =
  let href = Router.format("some-sub-path")
  Html.a [
    prop.text "Click me"
    prop.href href
    prop.onClick (goToUrl dispatch href)
  ]

Demo application

Here is a full example in an Elmish program.

type State = { CurrentUrl : string list }

type Msg =
    | UrlChanged of string list
    | NavigateToUsers
    | NavigateToUser of int

let init() = { CurrentUrl = Router.currentUrl() }, Cmd.none

let update msg state =
    match msg with
    | UrlChanged segments -> { state with CurrentUrl = segments }, Cmd.none
    // notice here the use of the command Cmd.navigate
    | NavigateToUsers -> state, Cmd.navigate("users")
    // Router.navigate with query string parameters
    | NavigateToUser userId -> state, Cmd.navigate("users", [ "id", userId ])

let render state dispatch =

    let currentPage =
        match state.CurrentUrl with
        | [ ] ->
            Html.div [
                Html.h1 "Home"
                Html.button [
                    prop.text "Navigate to users"
                    prop.onClick (fun _ -> dispatch NavigateToUsers)
                ]
                Html.a [
                    prop.href (Router.format("users"))
                    prop.text "Users link"
                ]
            ]
        | [ "users" ] ->
            Html.div [
                Html.h1 "Users page"
                Html.button [
                    prop.text "Navigate to User(10)"
                    prop.onClick (fun _ -> dispatch (NavigateToUser 10))
                ]
                Html.a [
                    prop.href (Router.format("users", ["id", 10]))
                    prop.text "Single User link"
                ]
            ]

        | [ "users"; Route.Query [ "id", Route.Int userId ] ] ->
            Html.h1 (sprintf "Showing user %d" userId)

        | _ ->
            Html.h1 "Not found"

    React.router [
        router.onUrlChanged (UrlChanged >> dispatch)
        router.children currentPage
    ]

Migrating from 2.x to 3.x

The 3.x release refactored the API to be more in line with how Feliz libraries are built as well as using latest features from React like functional components to implement the router itself. This release also made it easy to work with the router from a React-only applications and not just from Elmish based apps.

To migrate your router to latest version, here are the required changes:

  • Router.router becomes React.router
  • Router.onUrlChanged becomes router.onUrlChanged
  • Router.application becomes router.children
  • Router.navigate becomes Cmd.navigate for the Elmish variant and Router.navigate() : unit for the React variant

The rest of the implementation and API is kept as is. If you have any questions or run into problems, please let us know!

Migrating to v4

Starting from Feliz.Router v4, it will only work with Fable compiler v4+ and Feliz 2.x+

If you are using Fable compiler v3, then you should use Feliz.Router v3.10 (latest) which still relies on Feliz v1.68 for Fable v3 compatibility.

Development

# start by installing dependencies
npm install
# run tests
npm test

# run the demo application in watch mode
npm run start:demo

# build the demo application
npm run build:demo

feliz.router's People

Contributors

ameier38 avatar dependabot[bot] avatar dzoukr avatar erjanmx avatar kerams avatar marcpiechura avatar shmew avatar stroborobo avatar zaid-ajaj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

feliz.router's Issues

pathMode not working

Trying to get path mode working but router.onUrlChanged always passes the hash value.

Here is code to replicate on Feliz.Router 3.0.0

Router.currentPath() returns the expected value but router.onUrlChanged passes the hash mode segments to the callback funtion.

module App

open Elmish
open Elmish.React
open Feliz
open Feliz.Router

let parseUrl segments =
    let currentPath = Router.currentPath()
    sprintf "%A" currentPath, sprintf "%A" segments

type Msg =
    | UrlChanged of string * string

let init() = parseUrl <| Router.currentPath()

let update msg state =
    match msg with
    | UrlChanged (currentPath, currentUrl) -> currentPath, currentUrl

let render (currentPath: string, currentUrl: string) (dispatch: Msg -> unit) =
    React.router [
        router.pathMode
        router.onUrlChanged (parseUrl >> UrlChanged >> dispatch)
        router.children [
            Html.h1 "Router.currentPath(): "
            Html.p currentPath
            Html.h1 "segments from router.onUrlChanged: "
            Html.p currentUrl

            Html.a [
                prop.onClick (fun _ -> Router.navigatePath "/foo#bar")
                prop.children [ Html.p "change path" ]
            ]
        ]
    ]

#if DEBUG
printfn "Enabled HMR"
open Elmish.HMR
#endif

Program.mkSimple init update render
|> Program.withReactSynchronous "elmish-app"
|> Program.run

pathMode (a.k.a. historyMode): Allow full-refresh by setting webpack properly

Hello Zaid, thank's for your great repository.

In order to allow the refresh in webpack on a url in history mode, we can set the following property in the devServer:

    devServer: {
        publicPath: "/",
        contentBase: resolve(CONFIG.assetsDir),
        port: CONFIG.devServerPort,
        proxy: CONFIG.devServerProxy,
        hot: true,
        inline: true,
        historyApiFallback: true // <=====
    }

ref.: https://webpack.js.org/configuration/dev-server/#devserverhistoryapifallback

This allow to not have the 404 error message and also allow refresh (F5). I prefer the history mode to hashes.

In such case, we might also want to add in the index.html the following:

        <base href="/" target="_blank">

This will find the JS at the root path (note that it can also be injected using webpack, but I guess it's to the developer to know that).

How to handle query parameters?

Hi, thanks for the quick fix for path mode. It works well now.

Just wondering what is the best way of handing query parameters?

The docs show how to use pattern matching, but it doesn't allow query parameters to be re-ordered and makes it difficult for handing combinations where parameters may be missing. Even with only two optional parameters it becomes quick verbose to handle properly.

let fromValue, toValue =
    match segments =
    | [ Route.Query [ "from_value", fromValue; "to_value", toValue ] ] -> Some fromValue, Some toValue
    | [ Route.Query [ "to_value", toValue; "from_value", fromValue ] ] -> Some fromValue, Some toValue
    | [ Route.Query [ "to_value", toValue ] ] -> None, Some toValue
    | [ Route.Query [ "from_value", fromValue ] ] -> Some fromValue, None

Furthermore, even if you don't use query parameters, routing could break if unexpected query parameters are present such as facebook's fbclid. This particularly seems like it would be a big problem, right?

Start Routing from a base path

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

  • create a PR here and make it a part of Feliz.Router
  • or create and publish an independent package Feliz.Router.BasePath

What do you think?

Any plans for pre-release?

Hello my friend,

I am playing with the latest libraries for Fable v4 (currently in pre-release state) and having Feliz.Router in project fails with:

.\src\SAFEr.App.Client\.fable-build\fable_modules\Feliz.Router.3.8.0\Router.fs(952,13): (952,18) error FSHARP: The value, constructor, namespace or type 'ofSub' is not defined. Maybe you want one of the following:
client:    ofMsg (code 39)

Is there any plan for making pre-release for this library as well?

There should be a similar thing as navigate but returns a string

Router.navigate is really nice when navigating programmatically. However, it would be nice to have the same thing when setting the href on a a tag. Basically I would like to write:

Html.a [
    prop.href (Router.somethingLikeNavigate("apage"))
]

I can have a simple work around with prop.onClick but doesn't it make sense to have what I suggest?

Add a Router.Link component

It would be nice to have a built-in Link component for path mode that overrides the default anchor behavior (full refresh) with a history push instead, like react-router does. I would love to implement this, and would like to know your thoughts about it.

Router.format and Router.navigate for path mode?

Hi, it seems that both Router.format and Router.navigate seem to be hard coded to only work for RouteMode.Hash mode. Just wondering if that's by design or a bug, as I would like to use Router.navigate with RouteMode.Path mode.

Thanks.

Router.router usage?

Hello,

I am trying to use latest Feliz.Router version, but I do not understand how to migrate the following code from https://github.com/Zaid-Ajaj/login-with-url-extended/blob/18c41771e1fa68d865b2aaf1614b8e6640087f54/src/Home.fs#L145-L153:

Router.router [
    Router.onUrlChange (parseUrl >> UrlChanged >> dispatch)
    Router.application [
        Html.div [
            prop.style [ style.padding 20 ]
            prop.children [ activePage ]
        ]
    ]
]

It seems API has changed in version 3.0.

Thanks for your help

Poke @Shmew

(Question) pathMode without full redirect?

Hello friend,

amazing job on making routing look so easy! I love it and started to use pretty fast. There is still one think I probably fully don't understand. When creating link for routing in pathMode to page/sub-page, I have two options:

  1. Add onClick to link element which dispatch custom command like Navigate and in update function do router.navigate. Downside of this approach is there is no href in link so user cannot see in bottom browser bar where is it heading to + it isn't so SEO friendly.
  2. Use router.format to generate href for link, but clicking on those will trigger full redirect (combining with onClick doesn't help)

I am familiar with Elmish.Navigation where having such link does not create redirect.

Is it like that by design? Do you know any workaround how to have nicely looking links but keep routing internal without full redirect?

Thanks for any help.

Issue version 3.10.0 when using another Router component in the inner component

We have an elmish app uses
Fable 3.1.9 and following packages:


  <ItemGroup>
    <PackageReference Include="Fable.Elmish.React" Version="3.0.1" />
    <PackageReference Include="Fable.React" Version="7.2.0" />
    <PackageReference Include="Feliz.Router" Version="3.7.0" />
    <PackageReference Include="Feliz.UseDeferred" Version="1.4.1" />
    <PackageReference Include="Thoth.Json" Version="7.0.0" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Update="FSharp.Core" Version="5.0.1" />
  </ItemGroup>

This app uses a web component which was written in a separate project and will be injected as web component though the following api:
domEl "Component-X" ...

That component references the following packages:

  <ItemGroup>
    <PackageReference Include="Fable.Elmish.React" Version="3.0.1" />
    <PackageReference Include="Fable.React" Version="7.4.0" />
    <PackageReference Include="Fable.SimpleHttp" Version="3.5.0" />
    <PackageReference Include="Feliz.Router" Version="3.10.0" />
    <PackageReference Include="Feliz.UseDeferred" Version="1.5.0" />
    <PackageReference Include="Feliz.UseElmish" Version="1.5.1" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Update="FSharp.Core" Version="7.0.200" />
  </ItemGroup>

Component-X uses its own routing and recently we upgraded the its packages to upgraded the Feliz.Router to 3.10.0 of Feliz.Router.

After upgrading we found out that the routing at the application level doesn't work as before. Once the app navigated to Component-X and , from that time onward any url changes at the application level will raise two Router Url Changed events. One at the application level 's Router and the other at the Component-X 's Router level which interestingly the Url that is passed to the latter one is different from the one that is captured at application level. The url captured at Component-X level is base route of the component.
Let say we have the following routes at the application level
/path1
/path2
/Component-X

and the followings routes at the Component-X
/Component-X
/Component-X/Path3
/Component-X/Path4

When the app is loaded the routing to /path1 and /path3 (assume that the navigations are done through some hyperlinks) work correctly as long as we haven't touched any navigationto Component-X.
If we navigate to Component-X (Component-X is rendered ) then those hyperlinks which previously navigated us to /path1 and /path2 now navigate to Component-X.
If for example, you click the hyperlink that navigates you to /path1 then UrlChanged event occurs at App Router level and has the value /path1 and surprisingly the UrlChanged event happens at Component-X router level but with value /Component-X. In other versions before 3.10 the second UrlChanged event doesn't happen.

Per my investigation this error happens when the Component-X references just Feliz.Router 3.10.0 and all the other versions before that work correctly.

I can see that in the version 3.10.0 there is a major change to "No longer rely on Feliz.UseListener" and I am afraid that with this change we will face the same problem when we upgrade our app and components to fable version 4.0 in future.

Is it a bug with Feliz.Router version 3.10 upward or I need to change something in my App or Component?

Update Feliz.UseListener dependency to enable F# 5 builds

Would it be possible to update the dependency on Feliz.UseListener to 0.6.3? There doesn't seem to be any breaking changes from 0.6.1 as both minor version bumps were done to update Feliz.UseListener's dependencies. Right now I'm having issues with the F# 5 compiler because it seems to not want to recognize the library is installed because Feliz.UseListener's FSharp.Core dependency is invalid:

Detected package version outside of dependency constraint: Feliz.UseListener 0.6.1 requires FSharp.Core (>= 4.7.2 && < 5.0.0) but version FSharp.Core 5.0.0 was resolved.

I would do it myself and submit a PR, but I have no idea how to update a project using paket in order to make it update a dependency. Also I don't know how comfortable you'd be with an outsider submitting changes to dependencies and such.

Thanks for this awesome routing alternative, btw.

Example for routing doesnt route correctly?

I am trying the routing example in the Feliz docs, but doesnt seem to route to pages when hitting different paths (/counter, /index and so on) i am using Feliz template V4..

    /// <summary>
    /// A React component that uses Feliz.Router
    /// to determine what to show based on the current URL
    /// </summary>
    [<ReactComponent>]
    static member Router() =

        printfn "current path %A" Router.currentPath()
        printfn "current url %A" Router.currentUrl()

        let (currentUrl, updateUrl) = React.useState(Router.currentUrl())
        
        React.router [
            router.onUrlChanged updateUrl
            router.children [
                do
                    printfn "routing to url %A" currentUrl
                match currentUrl with
                | [ ] -> Html.h1 "Index"
                | [ "hello" ] -> Components.HelloWorld()
                | [ "counter" ] -> Components.Counter()
                | _ -> Html.h1 "Not found"
            ]
        ]

and then main is the simple one given but replaced the router component in place of the other

root.render(Components.Router())

Feliz.UseListener - unique overload could not be determined

Howdy again!

I've just updated to 3.0.1 and when I build, I'm getting the following errors:

ERROR in ./.fable/Feliz.UseListener.0.5.0/Listener.fs
Module Error (from ./node_modules/fable-loader/index.js):
/Users/james/Documents/realmweaver/realmweaver/.fable/Feliz.UseListener.0.5.0/Listener.fs(28,17): (28,90) error FSHARP: A unique overload for method 'addEventListener' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: abstract member EventTarget.addEventListener : type:string * listener:(Event -> unit) * ?options:AddEventListenerOptions -> unit, abstract member EventTarget.addEventListener : type:string * listener:(Event -> unit) * ?useCapture:bool -> unit (code 41)
 @ ./.fable/Feliz.Router.3.0.1/Router.fs 17:0-126 182:12-50 193:14-52 215:12-50 226:14-52 249:10-48 260:12-50
 @ ./src/Client/src/AppRoot/View.fs
 @ ./src/Client/src/Program.fs
 @ ./src/Client/Client.fsproj
 @ multi ./src/Client/Client.fsproj

ERROR in ./.fable/Feliz.UseListener.0.5.0/Listener.fs
Module Error (from ./node_modules/fable-loader/index.js):
/Users/james/Documents/realmweaver/realmweaver/.fable/Feliz.UseListener.0.5.0/Listener.fs(29,17): (29,93) error FSHARP: A unique overload for method 'removeEventListener' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: abstract member EventTarget.removeEventListener : type:string * listener:(Event -> unit) * ?options:RemoveEventListenerOptions -> unit, abstract member EventTarget.removeEventListener : type:string * listener:(Event -> unit) * ?useCapture:bool -> unit (code 41)
 @ ./.fable/Feliz.Router.3.0.1/Router.fs 17:0-126 182:12-50 193:14-52 215:12-50 226:14-52 249:10-48 260:12-50
 @ ./src/Client/src/AppRoot/View.fs
 @ ./src/Client/src/Program.fs
 @ ./src/Client/Client.fsproj
 @ multi ./src/Client/Client.fsproj

ERROR in ./.fable/Feliz.UseListener.0.5.0/Listener.fs
Module Error (from ./node_modules/fable-loader/index.js):
/Users/james/Documents/realmweaver/realmweaver/.fable/Feliz.UseListener.0.5.0/Listener.fs(37,17): (37,83) error FSHARP: A unique overload for method 'addEventListener' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: abstract member EventTarget.addEventListener : type:string * listener:(Event -> unit) * ?options:AddEventListenerOptions -> unit, abstract member EventTarget.addEventListener : type:string * listener:(Event -> unit) * ?useCapture:bool -> unit (code 41)
 @ ./.fable/Feliz.Router.3.0.1/Router.fs 17:0-126 182:12-50 193:14-52 215:12-50 226:14-52 249:10-48 260:12-50
 @ ./src/Client/src/AppRoot/View.fs
 @ ./src/Client/src/Program.fs
 @ ./src/Client/Client.fsproj
 @ multi ./src/Client/Client.fsproj

ERROR in ./.fable/Feliz.UseListener.0.5.0/Listener.fs
Module Error (from ./node_modules/fable-loader/index.js):
/Users/james/Documents/realmweaver/realmweaver/.fable/Feliz.UseListener.0.5.0/Listener.fs(42,17): (42,86) error FSHARP: A unique overload for method 'removeEventListener' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: abstract member EventTarget.removeEventListener : type:string * listener:(Event -> unit) * ?options:RemoveEventListenerOptions -> unit, abstract member EventTarget.removeEventListener : type:string * listener:(Event -> unit) * ?useCapture:bool -> unit (code 41)
 @ ./.fable/Feliz.Router.3.0.1/Router.fs 17:0-126 182:12-50 193:14-52 215:12-50 226:14-52 249:10-48 260:12-50
 @ ./src/Client/src/AppRoot/View.fs
 @ ./src/Client/src/Program.fs
 @ ./src/Client/Client.fsproj
 @ multi ./src/Client/Client.fsproj

ERROR in ./.fable/Feliz.UseListener.0.5.0/Listener.fs
Module Error (from ./node_modules/fable-loader/index.js):
/Users/james/Documents/realmweaver/realmweaver/.fable/Feliz.UseListener.0.5.0/Listener.fs(48,17): (48,83) error FSHARP: A unique overload for method 'addEventListener' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: abstract member EventTarget.addEventListener : type:string * listener:(Event -> unit) * ?options:AddEventListenerOptions -> unit, abstract member EventTarget.addEventListener : type:string * listener:(Event -> unit) * ?useCapture:bool -> unit (code 41)
 @ ./.fable/Feliz.Router.3.0.1/Router.fs 17:0-126 182:12-50 193:14-52 215:12-50 226:14-52 249:10-48 260:12-50
 @ ./src/Client/src/AppRoot/View.fs
 @ ./src/Client/src/Program.fs
 @ ./src/Client/Client.fsproj
 @ multi ./src/Client/Client.fsproj

ERROR in ./.fable/Feliz.UseListener.0.5.0/Listener.fs
Module Error (from ./node_modules/fable-loader/index.js):
/Users/james/Documents/realmweaver/realmweaver/.fable/Feliz.UseListener.0.5.0/Listener.fs(53,17): (53,86) error FSHARP: A unique overload for method 'removeEventListener' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: abstract member EventTarget.removeEventListener : type:string * listener:(Event -> unit) * ?options:RemoveEventListenerOptions -> unit, abstract member EventTarget.removeEventListener : type:string * listener:(Event -> unit) * ?useCapture:bool -> unit (code 41)
 @ ./.fable/Feliz.Router.3.0.1/Router.fs 17:0-126 182:12-50 193:14-52 215:12-50 226:14-52 249:10-48 260:12-50
 @ ./src/Client/src/AppRoot/View.fs
 @ ./src/Client/src/Program.fs
 @ ./src/Client/Client.fsproj
 @ multi ./src/Client/Client.fsproj

ERROR in ./.fable/Feliz.UseListener.0.5.0/Listener.fs
Module Error (from ./node_modules/fable-loader/index.js):
/Users/james/Documents/realmweaver/realmweaver/.fable/Feliz.UseListener.0.5.0/Listener.fs(59,17): (59,83) error FSHARP: A unique overload for method 'addEventListener' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: abstract member EventTarget.addEventListener : type:string * listener:(Event -> unit) * ?options:AddEventListenerOptions -> unit, abstract member EventTarget.addEventListener : type:string * listener:(Event -> unit) * ?useCapture:bool -> unit (code 41)
 @ ./.fable/Feliz.Router.3.0.1/Router.fs 17:0-126 182:12-50 193:14-52 215:12-50 226:14-52 249:10-48 260:12-50
 @ ./src/Client/src/AppRoot/View.fs
 @ ./src/Client/src/Program.fs
 @ ./src/Client/Client.fsproj
 @ multi ./src/Client/Client.fsproj

ERROR in ./.fable/Feliz.UseListener.0.5.0/Listener.fs
Module Error (from ./node_modules/fable-loader/index.js):
/Users/james/Documents/realmweaver/realmweaver/.fable/Feliz.UseListener.0.5.0/Listener.fs(64,17): (64,86) error FSHARP: A unique overload for method 'removeEventListener' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: abstract member EventTarget.removeEventListener : type:string * listener:(Event -> unit) * ?options:RemoveEventListenerOptions -> unit, abstract member EventTarget.removeEventListener : type:string * listener:(Event -> unit) * ?useCapture:bool -> unit (code 41)
 @ ./.fable/Feliz.Router.3.0.1/Router.fs 17:0-126 182:12-50 193:14-52 215:12-50 226:14-52 249:10-48 260:12-50
 @ ./src/Client/src/AppRoot/View.fs
 @ ./src/Client/src/Program.fs
 @ ./src/Client/Client.fsproj
 @ multi ./src/Client/Client.fsproj

Paket info:

    Feliz.Router (3.0.1)
      Fable.Core (>= 3.1.5)
      Fable.Elmish (>= 3.0.6)
      Feliz (>= 1.6)
      Feliz.UseListener (>= 0.3)
      FSharp.Core (>= 4.7.2)
    Feliz.UseListener (0.5)
      Fable.Core (>= 3.1.5 < 4.0)
      Feliz (>= 1.5 < 2.0)
      FSharp.Core (>= 4.7 < 5.0)

I don't get any errors in Ionide, but they're thrown when compiling the JS.

Honestly, not really sure what I can do/provide to help, so if you need any more info/context, please let me know.

Also downgraded t. 3.0.0 (no difference), but downgrading Feliz.UseListener to 0.4 allowed me to compile.

Trouble getting path-based functions to work

Thank you for Feliz.Router - it is one of many products from you that I am very grateful to use.

I am not sure if what I am about to describe is an issue with Feliz.Router. It is more likely to be an issue with me ...

Navigation works successfully on my SAFE Stack website using hash-based addresses, e.g. I can change from domain/ to domain/#/guide with no problem.

However, I am having trouble with SEO and Google recommends not using hashes in a URL, so I followed your suggestion about refactoring the application to use path-based functions instead. When I tested it locally, it worked, e.g. I could successfully change from localhost:8080 to localhost:8080/guide. But, when I uploaded the new version of the application to a web server, I was unable to navigate from the home page to any of the other linked pages as these pages could not be found. Is there something that I am overlooking?

Ampersand characters in query values need better handling

URL-decoding query strings in onUrlChanged before passing them to user code seems very nice, but there's one scenario you may not have considered: ampersands in query values. E.g., according to the documentation, the URL https://candystore.example.com/placeOrder?candyName=M%26M&quantity=5 would parse to ["placeOrder", "?candyName=M&M&quantity=5"], which would then confuse the Javascript URLSearchParams constructor when the end user tries to use the Route.Query active pattern. (I tested it on Google Chrome, and the result was that candyName had the value "M", and there was another key called M with the value "").

Since URLSearchParams already handles percent-decoding, I think it's better to not decode the query string in onUrlChanged, but just pass it unchanged to the user code, which can then safely use Route.Query to ensure a valid match against something like ["candyName", name] which will then place the string "M&M" into name.

Navigating to a Hashsign on a page with smooth scrolling

I can't navigate to an anchor inside a page. I would like to navigate on my landingpage by scrolling to the anchored sections using Ids in my divs. But, clicking a href with "#mySection" routes to /#mysection.
Is it possible to either integrate a navigateTo(hash) or to not route internal hashes?

Change URL only

Morning!

First off, I just want to say thanks for all of the work you've put in to the F#/Fable/Elmish community. I'm using a tonne of your libraries and the Dev Owl series & Elmish book really helped me get started.


I'm using Feliz.Router to detect URL changes and route accordingly, but within my app I've opted for the approach of routing via. messages (eg. AppRoute.HomePage or AppRoute.GetUser id) to better leverage compiler safety. However, I'm now a bit stuck as this method doesn't change the URL or alter the history.

Is there any way that I can manually alter the URL and history, without actually firing the onUrlChanged? I know I could emulate what the library is doing but it seems a bit redundant if there's already a way.

Not triggering UrlChanged on mount breaks pathMode routing

Hello friend,

I found a strange issue. When updating to Feliz.Router higher than 1.8.0, the routing is broken for Router.pathMode. There are two issues:

  1. Link created from Router.format is having hash instead of path (e.g. http://localhost:8080/#/login instead of http://localhost:8080/login)
  2. When entering manually routed address (like http://localhost:8080/login), the routing does not work at all.

I suspect this commit to be the 9da1d67 cause.

Is there any chance to have UrlChanged triggered on mount again, or do I need to stay with 1.8.0?

Errors with compiling, reason unclear

I hate raising these kinds of issues since there's always a good chance it's something on my end, but Nagareyama doesn't seem to want to compile Feliz.

I'm using it in an otherwise barebones project. VS doesn't report any errors, but dotnet fable does. I get about 50 of these errors. Happens even without any references to Feliz.Router in code.

Fable 3.1.5, latest Feliz.Router.

Any idea what it could be?

/.fable/Feliz.Router.3.5.0/Router.fs(950,9): (950,41) error FSHARP: This expression was expected to have type
    'Cmd<'Msg>'
but here has type
    ''a list'     (code 1)
/.fable/Feliz.Router.3.5.0/Router.fs(953,9): (953,54) error FSHARP: This expression was expected to have type
    'Cmd<'Msg>'
but here has type
    ''a list'     (code 1)

Nested routers

Hi, do you support nested routers? ex - top router handles /resource and in rendered component there are further definitions for /resource/show or /resource/edit. That way it's easy to manage http requests for resource data to show / edit / etc

Url change is not detected at initialization since version 3.0.0

Hello !

First of all, thank you for this useful library I discovered while reading the Elmish book.

My issue is about the use of this router in an Elmish project for handling the very first call of my single page application to handle forced redirections exactly like this section. In this chapter we implement a behavior which says "for the first call, if the user wants to go to a secured page, redirect it to the login page". We do it by detecting that the incoming url is the secured page so we tell the router to navigate to "login" page. This will trigger the onUrlChanged of the Feliz.Router which in turn triggers an UrlChanged message containing the new url, which will be processed by the update function.

When using a version up to 2.1.0 of Feliz.Router, after a forced navigation in my init function, a UrlChanged message is produced so I can get the new url in the update function. However, when using the version 3.0.0 (and higher) of this library, after a forced navigate in my init function, the data from the UrlChanged does not reflect the url change of my addressbar

I created a minimal repository with two versions of the same case. One uses Feliz.Router 2.1.0 and the second 3.0.0. The web app exposes a home page and a restricted page. My restricted page (localhost:8080/#/restricted) should NOT be accessible as a landing page so I put in init code to navigate to the main page. My update code retrieves the url change from the UrlChanged message and stores it in the state.

Here on version 2.1.0, after a call to localhost:8080/#/restricted my logic redirects me to the home page. Which is good since the url in the addressbar and the data of the state are matching

image

But when using version 3.0.0, the data of the state retrieved from the UrlChanged message does not match the url of the addressbar. From what I understand, the UrlChanged was message was not fired. Because the state contains data of the first time a UrlChanged was fired (containing the "restricted" segment).

image

These were the only leads I got since I am not able to debug further. Was that a change with version 3.0.0+ or I am missing something with this new version ?

Thanks

A common type for navigate and format?

Hey Zaid,

with the new format function I'd need to map my Route type twice to get a navigateTo and a toPath function. What do you think about a common type like Segments that contains the string list you're creating, so navigate and format could take the segments and the lib's users only need to map their Route to segments once?

Have a great day!

BeforeNavigation with cancellation (or something similar)

Hi Zaid!

A use case: The user is on a form, with some unsaved data, and clicks on a link that causes navigation (either by accident, or simply haven't thought about saving, because nowadays everything instant-syncs & saves data). If I'm correct, there is no way to catch this "event", and ask the user if they want to save, or just discard the data?

Or can I do this now? Either directly with Feliz.Router, or by subscribing to hashChange or similar event? Or if currently not supported, can I ask for this feature? :)

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.