Skip to content

Commit 2b43d1b

Browse files
committed
Update from Elm 0.18's Navigation.program to Elm 0.19's Browser.application
* adapt to the new Browser.application API in the following ways: * mirror the two-phase handling of URL changes in Browser.application's 'onUrlRequest' and 'onUrlChange' by bifurcating the RouterMsg variant of WrappedMsg into RouterMsgOnUrlChange and RouterMsgOnUrlRequestInternal variants. * add a slot to RouterModel to store the new Browser.Navigation.Key, and in the update function, use it to invoke Browser.Navigation.pushUrl in response to a urlRequestInternal. * create a new 'onExternalUrlRequest' function for the user to implement, since RouteUrl can handle internal requests for the user, but can't do anything sensible with external requests (as suggested by @basti1302). * eliminate the distinction between App and AppWithFlags, and all related duplication, as there is no variant of the new Browser.application that doesn't support flags. * make UrlChange more strongly typed, mirroring the structure of the Url.Url record type from elm/url, and rework the way UrlChanges are converted to Cmds with a new 'apply : Url -> UrlChange -> Url' function. * update all examples to work with the new API and 0.19 generally * include work-arounds for a couple of elm/url bugs (elm/url#37 and elm/url#17) * store the base path in ExampleViewer.Model to illustrate absolute path requirement of UrlChange * build the examples with '--debug' so users can get an idea for how they work under the hood * update README * remove references to complementary packages that aren't compatible with 0.19 (which is all of them) * remove the RouteUrl.Builder module and the use of the sporto/erl package, as this functionality is now largely provided by elm/url. * remove the older RouteHash module, as it was only present to ease the transition from elm-route-hash to elm-route-url. also remove example code illustrating its use.
1 parent 4e8247d commit 2b43d1b

31 files changed

+1120
-2525
lines changed

README.md

+23-52
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# elm-route-url
22

33
This is a module for routing single-page-apps in Elm, building on the
4-
[`elm-lang/navigation`](http://package.elm-lang.org/packages/elm-lang/navigation/latest)
4+
[`elm/browser`](https://package.elm-lang.org/packages/elm/browser/latest)
55
package.
66

77
## Rationale
@@ -18,30 +18,18 @@ So, there are two things going on here:
1818
* Mapping changes in the browser's location to changes in our app's state.
1919

2020
Now, you can already arrange for these things to happen using
21-
[`elm-lang/navigation`](http://package.elm-lang.org/packages/elm-lang/navigation/latest).
22-
Furthermore, there are already a wealth of complementary packages,
23-
such as:
24-
25-
* [evancz/url-parser](http://package.elm-lang.org/packages/evancz/url-parser/latest)
26-
* [Bogdanp/elm-combine](http://package.elm-lang.org/packages/Bogdanp/elm-combine/latest)
27-
* [Bogdanp/elm-route](http://package.elm-lang.org/packages/Bogdanp/elm-route/latest)
28-
* [etaque/elm-route-parser](http://package.elm-lang.org/packages/etaque/elm-route-parser/latest)
29-
* [poyang/elm-router](http://package.elm-lang.org/packages/poying/elm-router/latest)
30-
* [pzingg/elm-navigation-extra](http://package.elm-lang.org/packages/pzingg/elm-navigation-extra/latest)
31-
* [sporto/erl](http://package.elm-lang.org/packages/sporto/erl/latest)
32-
* [sporto/hop](http://package.elm-lang.org/packages/sporto/hop/latest)
33-
21+
[`elm/browser`](http://package.elm-lang.org/packages/elm/browser/latest).
3422
So, what does elm-route-url do differently than the others? First, I'll
3523
address this practically, then philosophically.
3624

3725

3826
### Mapping changes in the app state to a possible location change
3927

40-
If you were using [`elm-lang/navigation`](http://package.elm-lang.org/packages/elm-lang/navigation/latest)
28+
If you were using [`elm/browser`](https://package.elm-lang.org/packages/elm/browser/latest)
4129
directly, then you would make changes to the URL with ordinary commands.
4230
So, as you write your `update` function, you would possibly return a command,
43-
using [`modifyUrl`](http://package.elm-lang.org/packages/elm-lang/navigation/1.0.0/Navigation#modifyUrl)
44-
or [`newUrl`](http://package.elm-lang.org/packages/elm-lang/navigation/1.0.0/Navigation#newUrl).
31+
using [`replaceUrl`](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Navigation#replaceUrl)
32+
or [`pushUrl`](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Navigation#pushUrl).
4533

4634
Now, you can make this work, of course. However, the `update` function isn't
4735
really the perfect place to do this. Your update function looks like this:
@@ -89,11 +77,13 @@ use elm-route-url, you don't have to.
8977

9078
### Mapping location changes to messages our app can respond to
9179

92-
If you use the official [navigation](http://package.elm-lang.org/packages/elm-lang/navigation/latest)
93-
package in Elm 0.18 directly, you react to location changes by providing
94-
an argument to `Navigation.program` which converts a `Location` to a message
95-
your app can deal with. Those messages are then fed into your `update` function
96-
as the `Location` changes.
80+
If you use the official [browser](https://package.elm-lang.org/packages/elm/browser/latest/)
81+
package in Elm 0.19 directly, you react to location changes by providing
82+
two arguments to `Browser.application`: `onUrlRequest`, which converts a
83+
`UrlRequest` to a message your app can use to decide whether to permit the
84+
requested URL change; and `onUrlChange`, which converts a new `Url` to a
85+
message your app can deal with. Those messages are then fed into your
86+
`update` function as the `Url` changes.
9787

9888
On the surface, elm-route-url works in a similar manner, except that it
9989
asks you to implement a function which returns a list of messages.
@@ -108,8 +98,8 @@ location2messages : Location -> List Message
10898
`location2messages` will also be called when your `init` function is invoked,
10999
so you will also get access to the very first `Location`.
110100

111-
So, that is similar to how `Navigation` works. The difference is that
112-
`Navigation` will send you a message even when you programmatically change
101+
So, that is similar to how `Browser` works. The difference is that
102+
`Browser` will send you a message even when you programmatically change
113103
the URL. By contrast, elm-route-url only sends you messsages for **external**
114104
changes to the URL -- for instance, the user clicking on a link, opening
115105
a bookmark, or typing in the address bar. You won't get a message when you've
@@ -133,40 +123,21 @@ by Amitai Burstein, under the heading
133123

134124
## API
135125

136-
For the detailed API, see the documentation for `RouteUrl` and `RouteHash`
137-
(there are links to the right, if you're looking at the Elm package site).
138-
139-
The `RouteUrl` module is now the "primary" module. It gives you access to the
140-
whole `Location` object, and allows you to use the path, query and/or hash, as
141-
you wish.
126+
For the detailed API, see the documentation for `RouteUrl` (there's a link to
127+
the right, if you're looking at the Elm package site).
142128

143129
The main thing that elm-route-url handles is making sure that your
144130
`location2messages` and `delta2url` functions are called at the appropriate
145-
moment. How you parse the `Location` (and construct a `UrlChange`) is pretty
146-
much up to you. Now, I have included a `RouteUrl.Builder` module that could
147-
help with those tasks. However, you don't need to use it -- many other
148-
approaches would be possible, and there are links to helpful packages above.
149-
For my own part, I've been using [evancz/url-parser](http://package.elm-lang.org/packages/evancz/url-parser/latest)
150-
recently to implement `location2messages`.
151-
152-
The `RouteHash` module attempts to match the old API of elm-route-hash as
153-
closely as possible. You should be able to re-use your old `delta2update` and
154-
`location2action` functions without any changes. What will need to change is
155-
the code in your `main` module that initializes your app. The `RouteHash`
156-
module will probably be removed in a future version of elm-route-url, so you
157-
should migrate to using `RouteUrl` at an appropriate moment.
131+
moment. How you parse the `Url` (and construct a `UrlChange`) is pretty
132+
much up to you. You can use [`elm/url`](https://package.elm-lang.org/packages/elm/url/latest/)
133+
to help with those tasks.
158134

159135

160136
## Examples
161137

162138
I've included [example code](https://github.com/rgrempel/elm-route-hash/tree/master/examples/elm-architecture-tutorial)
163-
which turns the old Elm Architecture Tutorial (upgraded to Elm 0.18) into
164-
a single-page app. I've included three variations:
165-
166-
* Using the new `RouteUrl` API with the full path.
167-
* Using the new `RouteUrl` API with the hash only.
168-
* Using the old `RouteHash` API.
139+
which turns the old Elm Architecture Tutorial (upgraded to Elm 0.19) into
140+
a single-page app. I've included two variations:
169141

170-
Note that the example code makes heavy use of the `RouteUrl.Builder` module.
171-
However, as noted above, you don't necessarily need to use that -- a variety
172-
of alternative approaches are possible.
142+
* Using the `RouteUrl` API with the full path.
143+
* Using the `RouteUrl` API with the hash only.

elm-package.json

-23
This file was deleted.

elm.json

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"type": "package",
3+
"name": "rgrempel/elm-route-url",
4+
"summary": "Router for single-page-apps in Elm",
5+
"license": "MIT",
6+
"version": "5.0.0",
7+
"exposed-modules": [
8+
"RouteUrl"
9+
],
10+
"elm-version": "0.19.0 <= v < 0.20.0",
11+
"dependencies": {
12+
"ccapndave/elm-update-extra": "4.0.0 <= v < 5.0.0",
13+
"elm/browser": "1.0.0 <= v < 2.0.0",
14+
"elm/core": "1.0.0 <= v < 2.0.0",
15+
"elm/html": "1.0.0 <= v < 2.0.0",
16+
"elm/http": "1.0.0 <= v < 2.0.0",
17+
"elm/regex": "1.0.0 <= v < 2.0.0",
18+
"elm/url": "1.0.0 <= v < 2.0.0"
19+
},
20+
"test-dependencies": {}
21+
}

examples/elm-architecture-tutorial/Example1/Counter.elm

+31-60
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ module Example1.Counter exposing (..)
33
import Html exposing (..)
44
import Html.Attributes exposing (style)
55
import Html.Events exposing (onClick)
6-
import RouteHash exposing (HashUpdate)
7-
import RouteUrl.Builder exposing (Builder, builder, path, replacePath)
6+
import RouteUrl exposing (HistoryEntry(..), UrlChange(..))
87
import String exposing (toInt)
8+
import Url exposing (Url)
9+
910

1011

1112
-- MODEL
@@ -56,20 +57,19 @@ view : Model -> Html Action
5657
view model =
5758
div []
5859
[ button [ onClick Decrement ] [ text "-" ]
59-
, div [ countStyle ] [ text (toString model) ]
60+
, div countStyle [ text (String.fromInt model) ]
6061
, button [ onClick Increment ] [ text "+" ]
6162
]
6263

6364

64-
countStyle : Attribute any
65+
countStyle : List (Attribute any)
6566
countStyle =
66-
style
67-
[ ( "font-size", "20px" )
68-
, ( "font-family", "monospace" )
69-
, ( "display", "inline-block" )
70-
, ( "width", "50px" )
71-
, ( "text-align", "center" )
72-
]
67+
[ style "font-size" "20px"
68+
, style "font-family" "monospace"
69+
, style "display" "inline-block"
70+
, style "width" "50px"
71+
, style "text-align" "center"
72+
]
7373

7474

7575
{-| We add a separate function to get a title, which the ExampleViewer uses to
@@ -83,61 +83,32 @@ title =
8383

8484

8585

86-
-- Routing (Old API)
86+
-- Routing (New API)
8787

8888

89-
{-| For delta2update, we provide our state as the value for the URL.
90-
-}
91-
delta2update : Model -> Model -> Maybe HashUpdate
92-
delta2update previous current =
93-
Just <|
94-
RouteHash.set [ toString current ]
95-
89+
delta2builder : Model -> Model -> Maybe UrlChange
90+
delta2builder previous current =
91+
Just <| NewPath NewEntry <| { path = String.fromInt current, query = Nothing, fragment = Nothing }
9692

97-
{-| For location2action, we generate an action that will restore our state.
98-
-}
99-
location2action : List String -> List Action
100-
location2action list =
101-
case list of
102-
first :: rest ->
103-
case toInt first of
104-
Ok value ->
105-
[ Set value ]
106-
107-
Err _ ->
108-
-- If it wasn't an integer, then no action ... we could
109-
-- show an error instead, of course.
110-
[]
11193

112-
_ ->
113-
-- If nothing provided for this part of the URL, return empty list
94+
builder2messages : (Url -> Maybe String) -> Url -> List Action
95+
builder2messages extractPath url =
96+
case extractPath url of
97+
Nothing ->
11498
[]
11599

100+
Just path ->
101+
case String.split "/" path of
102+
first :: rest ->
103+
case toInt first of
104+
Just value ->
105+
[ Set value ]
116106

107+
Nothing ->
108+
-- If it wasn't an integer, then no action ... we could
109+
-- show an error instead, of course.
110+
[]
117111

118-
-- Routing (New API)
119-
120-
121-
delta2builder : Model -> Model -> Maybe Builder
122-
delta2builder previous current =
123-
builder
124-
|> replacePath [ toString current ]
125-
|> Just
126-
127-
128-
builder2messages : Builder -> List Action
129-
builder2messages builder =
130-
case path builder of
131-
first :: rest ->
132-
case toInt first of
133-
Ok value ->
134-
[ Set value ]
135-
136-
Err _ ->
137-
-- If it wasn't an integer, then no action ... we could
138-
-- show an error instead, of course.
112+
_ ->
113+
-- If nothing provided for this part of the URL, return empty list
139114
[]
140-
141-
_ ->
142-
-- If nothing provided for this part of the URL, return empty list
143-
[]

0 commit comments

Comments
 (0)