This repository has been archived by the owner on Mar 9, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 21
/
Main.elm
267 lines (205 loc) · 6.45 KB
/
Main.elm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
module Main exposing (..)
{-|
You will need Navigation, UrlParser and Hop.
```
elm package install elm-lang/navigation
elm package install evancz/url-parser
elm package install sporto/hop
```
-}
import Html exposing (..)
import Html.Attributes exposing (class)
import Html.Events exposing (onClick)
import Dict
import Navigation
import UrlParser exposing ((</>))
import Hop
import Hop.Types exposing (Config, Address, Query)
-- ROUTES
{-|
Define your routes as union types.
You need to provide a route for when the current URL doesn't match any known route i.e. NotFoundRoute.
-}
type Route
= AboutRoute
| MainRoute
| NotFoundRoute
{-|
Define route matchers.
See `docs/building-routes.md` for more examples.
-}
routes : UrlParser.Parser (Route -> a) a
routes =
UrlParser.oneOf
[ UrlParser.format MainRoute (UrlParser.s "")
, UrlParser.format AboutRoute (UrlParser.s "about")
]
{-|
Define your router configuration.
Use `hash = True` for hash routing e.g. `#/users/1`.
Use `hash = False` for push state e.g. `/users/1`.
The `basePath` is only used for path routing.
This is useful if you application is not located at the root of a url e.g. `/app/v1/users/1` where `/app/v1` is the base path.
-}
hopConfig : Config
hopConfig =
{ hash = True
, basePath = ""
}
-- MESSAGES
{-|
Add messages for navigation and changing the query.
-}
type Msg
= NavigateTo String
| SetQuery Query
-- MODEL
{-|
Add the current route and address to your model.
- `Route` is your Route union type defined above.
- `Hop.Address` is record to aid with changing the query string.
`route` will be used for determining the current route in the views.
`address` is needed because:
- Some navigation functions in Hop need this information to rebuild the current address.
- Your views might need information about the current query string.
-}
type alias Model =
{ address : Address
, route : Route
}
{-|
Respond to navigation messages in update i.e. NavigateTo and SetQuery
-}
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case (Debug.log "msg" msg) of
NavigateTo path ->
let
command =
-- First generate the URL using your config (`outputFromPath`).
-- Then generate a command using Navigation.newUrl.
Hop.outputFromPath hopConfig path
|> Navigation.newUrl
in
( model, command )
SetQuery query ->
let
command =
-- First modify the current stored address record (setting the query)
-- Then generate a URL using Hop.output
-- Finally, create a command using Navigation.newUrl
model.address
|> Hop.setQuery query
|> Hop.output hopConfig
|> Navigation.newUrl
in
( model, command )
{-|
Create a URL Parser for Navigation
-}
urlParser : Navigation.Parser ( Route, Address )
urlParser =
let
-- A parse function takes the normalised path from Hop after taking
-- in consideration the basePath and the hash.
-- This function then returns a result.
parse path =
-- First we parse using UrlParser.parse.
-- Then we return the parsed route or NotFoundRoute if the parsed failed.
-- You can choose to return the parse return directly.
path
|> UrlParser.parse identity routes
|> Result.withDefault NotFoundRoute
resolver =
-- Create a function that parses and formats the URL
-- This function takes 2 arguments: The Hop Config and the parse function.
Hop.makeResolver hopConfig parse
in
-- Create a Navigation URL parser
Navigation.makeParser (.href >> resolver)
{-|
Navigation will call urlUpdate when the address changes.
This function gets the result from `urlParser`, which is a tuple with (Route, Hop.Types.Address)
Address is a record that has:
```elm
{
path: List String,
query: Hop.Types.Query
}
```
- `path` is an array of strings that has the current path e.g. `["users", "1"]` for `"/users/1"`
- `query` Is dictionary of String String. You can access this information in your views to show the relevant content.
We store these two things in our model. We keep the address because it is needed for matching a query string.
-}
urlUpdate : ( Route, Address ) -> Model -> ( Model, Cmd Msg )
urlUpdate ( route, address ) model =
( { model | route = route, address = address }, Cmd.none )
-- VIEWS
view : Model -> Html Msg
view model =
div []
[ menu model
, pageView model
]
menu : Model -> Html Msg
menu model =
div []
[ div []
[ button
[ class "btnMain"
, onClick (NavigateTo "")
]
[ text "Main" ]
, button
[ class "btnAbout"
, onClick (NavigateTo "about")
]
[ text "About" ]
, button
[ class "btnQuery"
, onClick (SetQuery (Dict.singleton "keyword" "el/m"))
]
[ text "Set query string" ]
, currentQuery model
]
]
currentQuery : Model -> Html msg
currentQuery model =
let
query =
toString model.address.query
in
span [ class "labelQuery" ]
[ text query ]
{-|
Views can decide what to show using `model.route`.
-}
pageView : Model -> Html msg
pageView model =
case model.route of
MainRoute ->
div [] [ h2 [ class "title" ] [ text "Main" ] ]
AboutRoute ->
div [] [ h2 [ class "title" ] [ text "About" ] ]
NotFoundRoute ->
div [] [ h2 [ class "title" ] [ text "Not found" ] ]
-- APP
{-|
Your init function will receive an initial payload from Navigation, this payload is the initial matched location.
Here we store the `route` and `address` in our model.
-}
init : ( Route, Address ) -> ( Model, Cmd Msg )
init ( route, address ) =
( Model address route, Cmd.none )
{-|
Wire everything using Navigation.
-}
main : Program Never
main =
Navigation.program urlParser
{ init = init
, view = view
, update = update
, urlUpdate = urlUpdate
, subscriptions = (always Sub.none)
}