Reputation: 289
I am new to Elm and I really love it so far, but I've run into a problem that I cannot seem to wrap my head around.
I have an Html DOM, for example
div []
[ h1 [] [text "Headline 1"]
, p [] [text "Some text"]
, h2 [] [text "Headline 2"]
]
I would like to add a-links inside each h[1-6] element and so transform it to something like (keeping it simple)
div []
[ h1 [] [ text "Headline 1"
, [a [name "headline"] [text "#"]
]
, p [] [text "Some text"]
, h2 [] [text "Headline 2"
, [a [name "headline"] [text "#"]
]
]
This is conceptually not very hard. Look through the DOM, if element is h[1-6] add an a-link as child element. However my understanding of Elm is not well enough to get it to work.
Here is what I've been trying so far.
transform : Html a -> Html a
transform node =
-- check if the tag is h1-h6
case node.tag of
-- add a-link to h1 children
"h1" -> { node | children = (a [name "headline"] [text "#") :: node.children }
"h2" -> { node | children = (a [name "headline"] [text "#") :: node.children }
-- do this for all nodes in the tree
_ -> { node | children = List.map transform node.children }
This doesn't work.
The type annotation for `transform` does not match its definition.
40| transform : Html a -> Html a
^^^^^^^^^^^^^^^^
The type annotation is saying:
VirtualDom.Node a -> VirtualDom.Node a
But I am inferring that the definition has this type:
{ b | tag : String, children : List (Html a) }
-> { b | children : List (Html a), tag : String }
I understand that I can't do node.tag
because the generic type a
might not have that field. It wouldn't be type safe. For example the text node doesn't have a tag field, but is still an instance of Html.Html a
.
> text "Hello World"
{ type = "text", text = "Hello World" } : Html.Html a
My question is, how can I do this? Can I do this? or shouldn't I be doing this?
Upvotes: 2
Views: 491
Reputation: 6797
It is not possible to modify existing values of Html msg
type.
They are final internal structures, which are rendered by Virtual DOM in to actual HTML Nodes as an output of your program.
Html msg
is an alias for VirtualDom.Node a
You are attempting to use them as Records, but that's just a JavaScript object.
Elm REPL outputs String presentation of an abstract data structure here:
> text "Hello World"
{ type = "text", text = "Hello World" } : Html.Html a -- not a record
Instead of attempting to transform Html msg -> Html msg
, you should try something like:
-- Input example: [ "#", "http://google.com/", "http://package.elm-lang.org/" ]
linksView : List String -> Html msg
linksView links =
links
|> List.map (\link -> a [ href link ] [ text link ])
|> div [] -- Expected output: <div> with thre links
Upvotes: 2
Reputation: 36375
In Elm, Html a
is really only useful as output. You're never going to use it as input in the way that your transform
function is attempting.
You will be better served by creating a model to describe your domain, then passing that to a view
function to render html.
type alias Article =
{ priority : Priority
, headline : String
, body : String
}
type alias Model =
List Article
type Priority = First | Second
Your view could then look something like this:
view : Model -> Html msg
view =
div [] << List.map viewArticle
viewArticle : Article -> Html msg
viewArticle article =
let
priorityTag =
case article.priority of
First -> h1
Second -> h2
in
div []
[ priorityTag []
[ text article.headline
, a [ name "headline" ] [ text "#" ]
]
, p [] [ text article.body ]
]
Upvotes: 1