@sporto First of all Thanks a lot for this awesome tutorial. From start-to-end I didn't faced any discrepancy or errors executing the code given in tutorial on my dev machine.
Now coming to the issue I am facing:
In section Navigation approaches here you have mentioned the cons of using hash routing and as an alternative suggesting to use path routing which does look intuitive compared to the hash routing. I followed the example you referred to in https://github.com/sporto/elm-navigation-pushstate and updated my code such that it uses path routing.
The changes I made can be found below:
Msgs.elm
module Msgs exposing (..)
import Models exposing (Player)
import Navigation exposing (Location)
import RemoteData exposing (WebData)
type Msg
= OnFetchPlayers (WebData (List Player))
| OnLocationChange Location
| ChangeLocation String -- NOTE: Added this
Routing.elm
parseLocation : Location -> Route
parseLocation location =
case (parsePath matchers location) of -- NOTE: Updated `UrlParser.parseHash` with `UrlParser.parsePath` here
Just route ->
route
Nothing ->
NotFoundRoute
-- WHEN USING PATH-based Navigation
-- * the paths must start with a backward-slash (/)
-- * to match the path against a matcher use `UrlParser.parsePath` function
-- (refer parseLocation function above for e.g)
-- WHEN USING HASH-based Navigation
-- * the paths must start with a Hash-sign (#)
-- * to match the path against a matcher use `UrlParser.parseHash` function
-- (refer parseLocation function above for e.g)
-- Noticed the importance of / and # in README at
-- https://github.com/sporto/elm-navigation-pushstate#links
-- > Links should trigger a message to change the location when clicked, e.g. ChangeLocation "/users"
-- http://package.elm-lang.org/packages/elm-lang/navigation/2.1.0/Navigation#Location
-- > Note 2: These fields correspond exactly with the fields of document.location as described [here](https://developer.mozilla.org/en-US/docs/Web/API/Location)
-- where the targetLink mentions
-- ```
-- Location.pathname
-- Is a DOMString containing an initial '/' followed by the path of the URL..
--
-- Location.hash
-- Is a DOMString containing a '#' followed by the fragment identifier of the URL.
-- ```
playersPath : String
playersPath =
"/players"
playerPath : PlayerId -> String
playerPath id =
"/players/" ++ id
CustomHtmlEvents.elm (newly introduced)
module CustomHtmlEvents exposing (..)
import Html exposing (Attribute)
import Html.Events exposing (onWithOptions)
import Json.Decode as Decode
{-|
When clicking a link we want to prevent the default browser behaviour which is to load a new page.
So we use `onWithOptions` instead of `onClick`.
-}
onLinkClick : msg -> Attribute msg
onLinkClick message =
let
options =
{ stopPropagation = False
, preventDefault = True
}
in
onWithOptions "click" options (Decode.succeed message)
Players/List.elm
...
...
import CustomHtmlEvents exposing (onLinkClick)
...
...
..
editBtn : Player -> Html Msg
editBtn player =
let
path =
playerPath player.id
in
a
[ class "btn regular"
, href path
, onLinkClick (Msgs.ChangeLocation path) -- NOTE: Added this attribute
]
[ i [ class "fa fa-pencil mr1" ] [], text "Edit" ]
Players/Edit.elm
...
...
import CustomHtmlEvents exposing (onLinkClick)
...
...
..
listBtn : Html Msg
listBtn =
let
path =
playersPath
in
a
[ class "btn regular"
, href path
, onLinkClick (Msgs.ChangeLocation path) -- NOTE: Added this attribute
]
[ i [ class "fa fa-chevron-left mr1"] [], text "List" ]
And that does work! Please refer the screenshots attached while navigating:
1.png (top route)
![1](https://cloud.githubusercontent.com/assets/10440841/25437453/138f7bce-2ab4-11e7-8bc4-df0262699b2f.png)
2.png (Player route)
![2](https://cloud.githubusercontent.com/assets/10440841/25437454/13ae3280-2ab4-11e7-9e03-7f5ab7e8d776.png)
3.png (Players route accessed via clicking the List link on top-left in 2.png)
![3](https://cloud.githubusercontent.com/assets/10440841/25437455/13c64578-2ab4-11e7-9f67-51fea4d7fb11.png)
Now what I am unable to understand is that when I try to access routes clicking navigation links they work. However when I manually change the route in browser's address bar like
http://localhost:3000/players
I get following
![5](https://cloud.githubusercontent.com/assets/10440841/25437601/88643c6e-2ab4-11e7-8bf6-acbf52984614.png)
http://localhost:3000/players/2
I get following
![6](https://cloud.githubusercontent.com/assets/10440841/25437612/92589f26-2ab4-11e7-9777-11cbfc34b226.png)
http://localhost:3000/
- This one does work by showing the Listing.
http://localhost:3000/#players
- This one too works by showing the Listing but I am wondering why? Why this "hash" routing works?
However http://localhost:3000/#players/2
doesn't work.
So I am seeking your help in understanding the above behavior regarding manually changing the URL in address bar and they returning 404 response and how to fix them up to behave as expected i.e. whether I type in the URL or use navigation button or links they should show the desired data.
I have just started learning Elm and also novice to Functional Programming so please bear me on any questions I asked which sound silly to you.
Again Thank you so much for this wonderful tutorial. It demonstrates sheer clarity you have with this tutorial and is definitely one of the foremost resources one should refer to get started with Web App Development with Elm and at the same time getting acquainted the language's core concepts.