Skip to content

Commit

Permalink
Merge pull request #22 from rgrempel/pzingg-v0.18
Browse files Browse the repository at this point in the history
Further work on updating for Elm 0.18
  • Loading branch information
rgrempel authored Dec 8, 2016
2 parents 96c67bf + 903296b commit c03d0fa
Show file tree
Hide file tree
Showing 22 changed files with 583 additions and 573 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
elm-stuff
elm.js
elm.html
node_modules
67 changes: 44 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@

This is a module for routing single-page-apps in Elm, building on the
[`elm-lang/navigation`](http://package.elm-lang.org/packages/elm-lang/navigation/latest)
package. It is the successor to elm-route-hash:

* now compatible with Elm 0.18, and

* no longer limited to working only with the hash (hence the name change).

package.

## Rationale

Expand All @@ -32,10 +27,12 @@ such as:
* [Bogdanp/elm-route](http://package.elm-lang.org/packages/Bogdanp/elm-route/latest)
* [etaque/elm-route-parser](http://package.elm-lang.org/packages/etaque/elm-route-parser/latest)
* [poyang/elm-router](http://package.elm-lang.org/packages/poying/elm-router/latest)
* [pzingg/elm-navigation-extra](http://package.elm-lang.org/packages/pzingg/elm-navigation-extra/latest)
* [sporto/erl](http://package.elm-lang.org/packages/sporto/erl/latest)
* [sporto/hop](http://package.elm-lang.org/packages/sporto/hop/latest)

So, what does elm-route-url do differently than the others?
So, what does elm-route-url do differently than the others? First, I'll
address this practically, then philosophically.


### Mapping changes in the app state to a possible location change
Expand All @@ -59,6 +56,11 @@ want to ensure is that the URL reflects the final state of your model. (For
instance, consider a module with an `Increment` message and a `Decrement`
message. The URL doesn't care which way you arrived at a particular state).

Furthermore, every state of your model really ought to correspond with some URL.
That is, given some state of your model, there must be something that you'd like
to have appear in the URL. Or, to put it another way, what appears in the URL
really ought to be a function of your state, not the last message you received.

So, elm-route-url asks you to implement a function with a different signature:

```elm
Expand Down Expand Up @@ -88,28 +90,45 @@ use elm-route-url, you don't have to.
### Mapping location changes to messages our app can respond to

If you use the official [navigation](http://package.elm-lang.org/packages/elm-lang/navigation/latest)
package in Elm 0.18 directly, the `Navgation.program` differs from the
standard `Html.program` in two ways:

First, you are asked to implement an argument
to `Navigation.program` that converts a `Location` to a message
whenever the URL changes.

Second, the `Navigation.program` takes an init function that
takes a `Location` as an argument. This lets you use the URL on the first frame.
package in Elm 0.18 directly, you react to location changes by providing
an argument to `Navigation.program` which converts a `Location` to a message
your app can deal with. Those messages are then fed into your `update` function
as the `Location` changes.

In elm-route-url, this functionality for both of these is handled by asking
you to implement a function with a different signature:
On the surface, elm-route-url works in a similar manner, except that it
asks you to implement a function which returns a list of messages.
(This is possibly a convenience when you need multiple messages to
react to the URL change, though of course you could also redesign your
app to do multiple things with a single message).

```elm
location2messages : Location -> List Message
```

`location2messages` will be called when the underlying `Navigation.program`
`init` method is invoked, so you don't need to change that in your
program's code. And of course `location2messages` will also be called every
time the location is changed externally (not from a state change that
generated a new location via `delta2url`).
`location2messages` will also be called when your `init` function is invoked,
so you will also get access to the very first `Location`.

So, that is similar to how `Navigation` works. The difference is that
`Navigation` will send you a message even when you programmatically change
the URL. By contrast, elm-route-url only sends you messsages for **external**
changes to the URL -- for instance, the user clicking on a link, opening
a bookmark, or typing in the address bar. You won't get a message when you've
made a change in the URL due to your `delta2url` function, since your state
is already in sync with that URL -- no message is required.


### Philosphically

You can, if you are so inclined, think about those differences in a more
philosophical way. There is a [thread](https://groups.google.com/forum/#!topic/elm-discuss/KacB1VqkVJg/discussion)
on the Elm mailing list where Erik Lott gives an excellent summary.
The question, he says, is whether the address bar should drive the model,
or whether the model should drive the address bar. For more details,
read the thread -- it really is a very good summary.

Another nice discussion of the philosophy behind elm-route-url is in a blog post
by Amitai Burstein, under the heading
[URL Change is not Routing](http://www.gizra.com/content/thinking-choosing-elm/#url-change-is-not-routing)


## API
Expand All @@ -127,6 +146,8 @@ moment. How you parse the `Location` (and construct a `UrlChange`) is pretty
much up to you. Now, I have included a `RouteUrl.Builder` module that could
help with those tasks. However, you don't need to use it -- many other
approaches would be possible, and there are links to helpful packages above.
For my own part, I've been using [evancz/url-parser](http://package.elm-lang.org/packages/evancz/url-parser/latest)
recently to implement `location2messages`.

The `RouteHash` module attempts to match the old API of elm-route-hash as
closely as possible. You should be able to re-use your old `delta2update` and
Expand Down
1 change: 1 addition & 0 deletions elm-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
],
"exposed-modules": [
"RouteUrl",
"RouteHash",
"RouteUrl.Builder"
],
"dependencies": {
Expand Down
32 changes: 14 additions & 18 deletions examples/elm-architecture-tutorial/Example1/Counter.elm
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,20 @@ type alias Model =
Int



-- Added from Main.elm


{-| Added from Main.elm
-}
init : Model
init =
0



-- UPDATE
-- We add a Set action for the advanced example, so that we
-- can restore a particular bookmarked state.


{-| We add a Set action for the advanced example, so that we
can restore a particular bookmarked state.
-}
type Action
= Increment
| Decrement
Expand Down Expand Up @@ -73,33 +72,30 @@ countStyle =
]



-- We add a separate function to get a title, which the ExampleViewer uses to
-- construct a table of contents. Sometimes, you might have a function of this
-- kind return `Html` instead, depending on where it makes sense to do some of
-- the construction.


{-| We add a separate function to get a title, which the ExampleViewer uses to
construct a table of contents. Sometimes, you might have a function of this
kind return `Html` instead, depending on where it makes sense to do some of
the construction.
-}
title : String
title =
"Counter"



-- Routing (Old API)
-- For delta2update, we provide our state as the value for the URL


{-| For delta2update, we provide our state as the value for the URL.
-}
delta2update : Model -> Model -> Maybe HashUpdate
delta2update previous current =
Just <|
RouteHash.set [ toString current ]



-- For location2action, we generate an action that will restore our state


{-| For location2action, we generate an action that will restore our state.
-}
location2action : List String -> List Action
location2action list =
case list of
Expand Down
23 changes: 11 additions & 12 deletions examples/elm-architecture-tutorial/Example2/Counter.elm
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ init count =


-- UPDATE
-- We add a Set action for the advanced example, so that we
-- can restore a particular bookmarked state.


{-| We add a Set action for the advanced example, so that we
can restore a particular bookmarked state.
-}
type Action
= Increment
| Decrement
Expand Down Expand Up @@ -81,19 +82,18 @@ countStyle =


-- Routing (Old API)
-- For delta2update, we provide our state as the value for the URL


{-| For delta2update, we provide our state as the value for the URL
-}
delta2update : Model -> Model -> Maybe HashUpdate
delta2update previous current =
Just <|
RouteHash.set [ toString current ]



-- For location2action, we generate an action that will restore our state


{-| For location2action, we generate an action that will restore our state
-}
location2action : List String -> List Action
location2action list =
case list of
Expand All @@ -113,18 +113,17 @@ location2action list =


-- Routing (New API)
-- We'll just send back a string


{-| We'll just send back a string
-}
delta2fragment : Model -> Model -> String
delta2fragment previous current =
toString current



-- We'll just take a string


{-| We'll just take a string
-}
fragment2messages : String -> List Action
fragment2messages fragment =
case toInt fragment of
Expand Down
26 changes: 12 additions & 14 deletions examples/elm-architecture-tutorial/Example2/CounterPair.elm
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ type alias Model =
}



-- Rewrote to move initialization from Main.elm


{-| Rewrote to move initialization from Main.elm
-}
init : Model
init =
{ topCounter = Counter.init 0
Expand Down Expand Up @@ -68,24 +66,23 @@ view model =
]



-- We add a separate function to get a title, which the ExampleViewer uses to
-- construct a table of contents. Sometimes, you might have a function of this
-- kind return `Html` instead, depending on where it makes sense to do some of
-- the construction.


{-| We add a separate function to get a title, which the ExampleViewer uses to
construct a table of contents. Sometimes, you might have a function of this
kind return `Html` instead, depending on where it makes sense to do some of
the construction.
-}
title : String
title =
"Pair of Counters"



-- Routing (Old API)
-- To encode state in the URL, we'll just delegate & concatenate
-- This will produce partial URLs like /6/7


{-| To encode state in the URL, we'll just delegate & concatenate
This will produce partial URLs like /6/7
-}
delta2update : Model -> Model -> Maybe HashUpdate
delta2update previous current =
-- The implementation is not especially elegant ... perhaps
Expand Down Expand Up @@ -116,9 +113,10 @@ location2action list =


-- Routing (New API)
-- We'll put the two counters in the query parameters, just for fun


{-| We'll put the two counters in the query parameters, just for fun
-}
delta2builder : Model -> Model -> Maybe Builder
delta2builder previous current =
builder
Expand Down
28 changes: 14 additions & 14 deletions examples/elm-architecture-tutorial/Example3/CounterList.elm
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ init =


-- UPDATE
-- Add an action for the advanced example to set our
-- state from a `List Int`


{-| Add an action for the advanced example to set our
state from a `List Int`
-}
type Action
= Insert
| Remove
Expand Down Expand Up @@ -110,27 +111,26 @@ viewCounter ( id, model ) =
Html.map (Modify id) (Counter.view model)



-- We add a separate function to get a title, which the ExampleViewer uses to
-- construct a table of contents. Sometimes, you might have a function of this
-- kind return `Html` instead, depending on where it makes sense to do some of
-- the construction.


{-| We add a separate function to get a title, which the ExampleViewer uses to
construct a table of contents. Sometimes, you might have a function of this
kind return `Html` instead, depending on where it makes sense to do some of
the construction.
-}
title : String
title =
"List of Counters"



-- Routing (Old API)
-- You could do this in a variety of ways. We'll ignore the ID's, and just
-- encode the value of each Counter in the list -- so we'll end up with
-- something like /0/1/5 or whatever. When we recreate that, we won't
-- necessarily have the same IDs, but that doesn't matter for this example.
-- If it mattered, we'd have to do this a different way.


{-| You could do this in a variety of ways. We'll ignore the ID's, and just
encode the value of each Counter in the list -- so we'll end up with
something like /0/1/5 or whatever. When we recreate that, we won't
necessarily have the same IDs, but that doesn't matter for this example.
If it mattered, we'd have to do this a different way.
-}
delta2update : Model -> Model -> Maybe HashUpdate
delta2update previous current =
-- We'll take advantage of the fact that we know that the counter
Expand Down
Loading

0 comments on commit c03d0fa

Please sign in to comment.