To build a real Web App
, of course we’ll want to display different content with different Url
, not just static links like the homepage /
or /about
page but what about links that contain query parameters such as /search?q=seiza
.
To create such navigation logic based on links containing query parameters, we need to learn how to use some more tools provided by the package elm/url
that we installed earlier. And here, we will continue to look at the example isoloaded by Elm
in a series of tutorials on their official website.
Example 1
Let’s say we have a website
and the path
listed below are all valid.
/topic/architecture
/topic/painting
/topic/sculpture
/blog/42
/blog/123
/blog/451
/user/tom
/user/sue
/user/sue/comment/11
/user/sue/comment/51
So we have topic
category pages, blog
post pages, user
profile pages, and methods to search for user comments. And now we have the example code that uses the module Url.Parser
to write a program that parses the Url
path as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">type</span> <span class="token constant">Route</span> <span class="token operator">=</span> <span class="token constant">Topic</span> <span class="token constant">String</span> <span class="token operator">|</span> <span class="token constant">Blog</span> <span class="token constant">Int</span> <span class="token operator">|</span> <span class="token constant">User</span> <span class="token constant">String</span> <span class="token operator">|</span> <span class="token constant">Comment</span> <span class="token constant">String</span> <span class="token constant">Int</span> <span class="token hvariable">routeParser</span> <span class="token operator">:</span> <span class="token constant">Parser</span> <span class="token punctuation">(</span> <span class="token constant">Route</span> <span class="token operator">-></span> <span class="token hvariable">a</span> <span class="token punctuation">)</span> <span class="token hvariable">a</span> <span class="token hvariable">routeParser</span> <span class="token operator">=</span> <span class="token hvariable">oneOf</span> <span class="token punctuation">[</span> <span class="token hvariable">map</span> <span class="token constant">Topic</span> <span class="token punctuation">(</span> <span class="token hvariable">s</span> <span class="token string">"topic"</span> <span class="token operator"></></span> <span class="token hvariable">string</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token hvariable">map</span> <span class="token constant">Blog</span> <span class="token punctuation">(</span> <span class="token hvariable">s</span> <span class="token string">"blog"</span> <span class="token operator"></></span> <span class="token hvariable">int</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token hvariable">map</span> <span class="token constant">User</span> <span class="token punctuation">(</span> <span class="token hvariable">s</span> <span class="token string">"user"</span> <span class="token operator"></></span> <span class="token hvariable">string</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token hvariable">map</span> <span class="token constant">Comment</span> <span class="token punctuation">(</span> <span class="token hvariable">s</span> <span class="token string">"user"</span> <span class="token operator"></></span> <span class="token hvariable">string</span> <span class="token operator"></></span> <span class="token hvariable">s</span> <span class="token string">"comment"</span> <span class="token operator"></></span> <span class="token hvariable">int</span> <span class="token punctuation">)</span> <span class="token punctuation">]</span> |
We have a routeParser
function that takes no input parameters and will return a Parser
initializer with the specific style information Parser (Route -> a) a
. All the functions that support s
, </>
, string
, int
, are import
from the module Url.Parser
and are designed to have a syntax used at the code surface that corresponds to the format of the path
that we need to split. get query parameters.
Ok, so with the syntax to represent the path
type, we need to separate the parameters as above, then we have:
- Static strings in
path
are described by function callss
- Function calls
</>
correspond to the slashes/
inpath
- And the functions
string
,int
, describe where to split query parameters and return data type
And this is the output of the Parser
returned by the routeParser
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token comment">-- /topic/pottery ==> Just (Topic "pottery")</span> <span class="token comment">-- /topic/collage ==> Just (Topic "collage")</span> <span class="token comment">-- /topic/ ==> Nothing</span> <span class="token comment">-- /blog/42 ==> Just (Blog 42)</span> <span class="token comment">-- /blog/123 ==> Just (Blog 123)</span> <span class="token comment">-- /blog/mosaic ==> Nothing</span> <span class="token comment">-- /user/tom/ ==> Just (User "tom")</span> <span class="token comment">-- /user/sue/ ==> Just (User "sue")</span> <span class="token comment">-- /user/ ==> Nothing</span> <span class="token comment">-- /user/bob/comment/42 ==> Just (Comment "bob" 42)</span> <span class="token comment">-- /user/sam/comment/35 ==> Just (Comment "sam" 35)</span> <span class="token comment">-- /user/sam/comment/ ==> Nothing</span> |
Reading the definition code of the functions used in the module Url.Parser
also important but not necessary at the outset, as we have seen the syntax used in a very declarative
visual description, easy to understand and apply without having to read the specific sequence of operations of the function calls here.
Example 2
Now another example case where we have a personal blog with the following valid links:
/blog/12/the-history-of-chairs
/blog/13/the-endless-september
/blog/14/whale-facts
/blog/
/blog?q=whales
/blog?q=seiza
In this case, we have article pages /blog/number/
and search results pages /blog?q=...
with the query parameter q=
. To separate the optional query explorer parameters at the key position q=
, we need to use the module Url.Parser.Query
.
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token import-statement"><span class="token keyword">import</span> Url.Parser <span class="token keyword">exposing</span> </span><span class="token punctuation">(</span> <span class="token constant">Parser</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token operator"></></span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token operator"><?></span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token hvariable">int</span> <span class="token punctuation">,</span> <span class="token hvariable">map</span> <span class="token punctuation">,</span> <span class="token hvariable">oneOf</span> <span class="token punctuation">,</span> <span class="token hvariable">s</span> <span class="token punctuation">,</span> <span class="token hvariable">string</span> <span class="token punctuation">)</span> <span class="token import-statement"><span class="token keyword">import</span> Url.Parser.Query <span class="token keyword">as</span> Query</span> <span class="token keyword">type</span> <span class="token constant">Route</span> <span class="token operator">=</span> <span class="token constant">BlogPost</span> <span class="token constant">Int</span> <span class="token constant">String</span> <span class="token operator">|</span> <span class="token constant">BlogQuery</span> <span class="token punctuation">(</span> <span class="token constant">Maybe</span> <span class="token constant">String</span> <span class="token punctuation">)</span> <span class="token hvariable">routeParser</span> <span class="token operator">:</span> <span class="token constant">Parser</span> <span class="token punctuation">(</span> <span class="token constant">Route</span> <span class="token operator">-></span> <span class="token hvariable">a</span> <span class="token punctuation">)</span> <span class="token hvariable">a</span> <span class="token hvariable">routeParser</span> <span class="token operator">=</span> <span class="token hvariable">oneOf</span> <span class="token punctuation">[</span> <span class="token hvariable">map</span> <span class="token constant">BlogPost</span> <span class="token punctuation">(</span> <span class="token hvariable">s</span> <span class="token string">"blog"</span> <span class="token operator"></></span> <span class="token hvariable">int</span> <span class="token operator"></></span> <span class="token hvariable">string</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token hvariable">map</span> <span class="token constant">BlogQuery</span> <span class="token punctuation">(</span> <span class="token hvariable">s</span> <span class="token string">"blog"</span> <span class="token operator"><?></span> <span class="token hvariable">Query.string</span> <span class="token string">"q"</span> <span class="token punctuation">)</span> <span class="token punctuation">]</span> |
The syntax applied in this case is also completely visible to the type of path
described in the code. And this is the output of the Parser
returned by the routeParser
function.
1 2 3 4 5 6 7 | `/blog/12/the-history-of-chairs` ==> Just (BlogPost 12 "the-history-of-chairs") `/blog/13/the-endless-september` ==> Just (BlogPost 13 "the-endless-september") `/blog/14/whale-facts` ==> Just (BlogPost 14 "whale-facts") `/blog/` ==> Just (BlogQuery Nothing) `/blog?q=whales` ==> Just (BlogQuery (Just "whales")) `/blog?q=seiza` ==> Just (BlogQuery (Just "seiza")) |
Example 3
And the last example is that we have a website in the form of a general document page with the following valid links:
/Basics
/Maybe
/List
/List#map
/List#filter
/List#foldl
In this case, we are having paths containing the id
identifiers of a certain HTML
element. And to separate these id
, we will need to use the fragment
function in the module Url.Parser
.
1 2 3 4 5 | <span class="token keyword">type</span> <span class="token keyword">alias</span> <span class="token constant">Docs</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token constant">String</span> <span class="token punctuation">,</span> <span class="token constant">Maybe</span> <span class="token constant">String</span> <span class="token punctuation">)</span> <span class="token hvariable">docsParser</span> <span class="token operator">:</span> <span class="token constant">Parser</span> <span class="token punctuation">(</span> <span class="token constant">Docs</span> <span class="token operator">-></span> <span class="token hvariable">a</span> <span class="token punctuation">)</span> <span class="token hvariable">a</span> <span class="token hvariable">docsParser</span> <span class="token operator">=</span> <span class="token hvariable">map</span> <span class="token hvariable">Tuple.pair</span> <span class="token punctuation">(</span> <span class="token hvariable">string</span> <span class="token operator"></></span> <span class="token hvariable">fragment</span> <span class="token builtin">identity</span> <span class="token punctuation">)</span> |
So we have the </> fragment identity
function calls to start extracting the #id
at the end of the path
, which is a bit special. However we can also read in a simple way, </> fragment
is the #
symbol, and identity
is the position of the id
string to be split in the path
. And here is the corresponding performance result.
1 2 3 4 5 6 7 | -- /Basics ==> Just ("Basics", Nothing) -- /Maybe ==> Just ("Maybe", Nothing) -- /List ==> Just ("List", Nothing) -- /List#map ==> Just ("List", Just ("map")) -- /List#filter ==> Just ("List", Just ("filter")) -- /List#foldl ==> Just ("List", Just ("foldl")) |
So we also know how to use the module Url
to separate the query parameters in the path
so that we can use it for processing logic in the update
function of Browse.application
.
The update
then creates a new model
record corresponding to the query information so that the view
can adjust the display of the single page to suit the user’s requirements. The rest of the story now is that we don’t have a way to change the content of the <head>
element yet because Elm
doesn’t have any module
to support it.
[Declarative Programming + Elm] Lesson 17 – JavaScript Interop