Interpretación de URLs
En una aplicación real nos interesa mostrar distinto contenido para distintas URLs:
/search
/search?q=seiza
/settings
¿Cómo lo logramos? Usando el paquete elm/url
para interpretar strings como estructuras de datos Elm. Este paquete se entiende mejor al mirar ejemplos, así que entremos directamente a ello.
Ejemplo 1
Digamos que tenemos un sitio web sobre arte donde las siguientes direcciones deben ser válidas:
/topic/architecture
/topic/painting
/topic/sculpture
/blog/42
/blog/123
/blog/451
/user/tom
/user/sue
/user/sue/comment/11
/user/sue/comment/51
Tenemos páginas de temas específicos, artículos del blog, información de usuarios y una forma de ver comentarios escritos por usuarios específicos. Vamos a usar el módulo Url.Parser
para escribir un interpretador de URLs como el siguiente:
import Url.Parser exposing ((</>), Parser, int, map, oneOf, s, string)
type Route
= Topic String
| Blog Int
| User String
| Comment String Int
routeParser : Parser (Route -> a) a
routeParser =
oneOf
[ map Topic (s "topic" </> string)
, map Blog (s "blog" </> int)
, map User (s "user" </> string)
, map Comment (s "user" </> string </> s "comment" </> int)
]
-- /topic/pottery ==> Just (Topic "pottery")
-- /topic/collage ==> Just (Topic "collage")
-- /topic/ ==> Nothing
-- /blog/42 ==> Just (Blog 42)
-- /blog/123 ==> Just (Blog 123)
-- /blog/mosaic ==> Nothing
-- /user/tom/ ==> Just (User "tom")
-- /user/sue/ ==> Just (User "sue")
-- /user/bob/comment/42 ==> Just (Comment "bob" 42)
-- /user/sam/comment/35 ==> Just (Comment "sam" 35)
-- /user/sam/comment/ ==> Nothing
-- /user/ ==> Nothing
El módulo Url.Parser
permite interpretar URLs válidas como datos Elm en forma muy concisa.
Ejemplo 2
Ahora imaginemos que tenemos un blog personal donde direcciones como las siguientes son válidas:
/blog/12/the-history-of-chairs
/blog/13/the-endless-september
/blog/14/whale-facts
/blog/
/blog?q=whales
/blog?q=seiza
En este caso tenemos artículos y una página principal que lleva opcionalmente un parámetro q
de consulta. Tenemos que añadir el módulo Url.Parser.Query
para escribir nuestro interpretador:
import Url.Parser exposing ((</>), (<?>), Parser, int, map, oneOf, s, string)
import Url.Parser.Query as Query
type Route
= BlogPost Int String
| BlogQuery (Maybe String)
routeParser : Parser (Route -> a) a
routeParser =
oneOf
[ map BlogPost (s "blog" </> int </> string)
, map BlogQuery (s "blog" <?> Query.string "q")
]
-- /blog/14/whale-facts ==> Just (BlogPost 14 "whale-facts")
-- /blog/14 ==> Nothing
-- /blog/whale-facts ==> Nothing
-- /blog/ ==> Just (BlogQuery Nothing)
-- /blog ==> Just (BlogQuery Nothing)
-- /blog?q=chabudai ==> Just (BlogQuery (Just "chabudai"))
-- /blog/?q=whales ==> Just (BlogQuery (Just "whales"))
-- /blog/?query=whales ==> Just (BlogQuery Nothing)
Los operadores </>
y <?>
nos permiten escribir intepretadores que se ven como las URLs que queremos interpretar. Y el uso de Url.Parser.Query
nos permitió manejar parámetros como ?q=seiza
.
Ejemplo 3
Ahora lo que tenemos es un sitio web de documentación donde veremos direcciones como estas:
/Basics
/Maybe
/List
/List#map
/List#filter
/List#foldl
Podemos usar el interpretador fragment
de Url.Parser
para interpretar direcciones, así:
type alias Docs =
( String, Maybe String )
docsParser : Parser (Docs -> a) a
docsParser =
map Tuple.pair (string </> fragment identity)
-- /Basics ==> Just ("Basics", Nothing)
-- /Maybe ==> Just ("Maybe", Nothing)
-- /List ==> Just ("List", Nothing)
-- /List#map ==> Just ("List", Just "map")
-- /List# ==> Just ("List", Just "")
-- /List/map ==> Nothing
-- / ==> Nothing
Y así ya podemos interpretar “fragmentos” de URL también.
Síntesis
Ya vimos varios interpretadores, así que ahora nos toca ver cómo se conjugan en un programa Browser.application
. En vez de sólo guardar la URL como lo hicimos en el ejemplo anterior, veamos si podemos interpretar la URL y convertirla en datos útiles para mostrar.
TODO
Lo nuevo más importante es:
- Nuestra función
update
interpreta la URL cuando recibe un mensajeUrlChanged
. - Nuestra función
view
muestra distinto contenido para direcciones distintas.
No es nada muy complicado, ¿no?
Pero ¿qué pasa si tengo 10, o 20, o 100 distintas páginas? ¿Tiene todo que ir dentro de esta función view
? Seguro que no puede ir todo en el mismo archivo. ¿En cuántos archivos se separa? ¿Cómo debe ser la estructura de directorios? Bueno, eso mismo es lo que vamos a discutir en el próximo capítulo.