2016-08-05 36 views
2

Ich bin neu in Elm und ich liebe es so weit, aber ich habe ein Problem, dass ich nicht scheinen kann, um meinen Kopf zu wickeln.Transform Html DOM

Ich habe einen HTML-DOM, zum Beispiel

div [] 
    [ h1 [] [text "Headline 1"] 
    , p [] [text "Some text"] 
    , h2 [] [text "Headline 2"] 
    ] 

I a-Links in jedem h hinzufügen möchte [1-6] Element und so verwandelt es so etwas wie (es einfach zu halten)

div [] 
    [ h1 [] [ text "Headline 1" 
      , [a [name "headline"] [text "#"] 
      ] 
    , p [] [text "Some text"] 
    , h2 [] [text "Headline 2" 
      , [a [name "headline"] [text "#"] 
      ] 
    ] 

Dies ist konzeptionell nicht sehr schwer. Schauen Sie sich das DOM an, wenn Element h [1-6] ist, fügen Sie eine A-Verknüpfung als untergeordnetes Element hinzu. Allerdings ist mein Verständnis von Elm nicht gut genug, um es zur Arbeit zu bringen.

Hier ist was ich bisher versucht habe.

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 } 

Dies funktioniert nicht.

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 } 

Ich verstehe, dass ich nicht node.tag tun kann, weil der generische Typ a nicht das Feld haben könnte. Es wäre nicht typsicher. Zum Beispiel hat der Textknoten kein Tag-Feld, ist aber immer noch eine Instanz von Html.Html a.

> text "Hello World" 
{ type = "text", text = "Hello World" } : Html.Html a 

Meine Frage ist, wie kann ich das tun? Darf ich das machen? oder sollte ich das nicht tun?

Antwort

2

Es ist nicht möglich, vorhandene Werte des Typs Html msg zu ändern.

Sie sind endgültige interne Strukturen, die von Virtual DOM in tatsächlichen HTML-Knoten als eine Ausgabe Ihres Programms gerendert werden.

Html msg ist ein Alias ​​für VirtualDom.Node a

Sie versuchen, sie als Datensätze zu verwenden, aber das ist nur ein Objekt JavaScript.

Elm REPL gibt String Darstellung einer abstrakten Datenstruktur hier:

> text "Hello World" 
{ type = "text", text = "Hello World" } : Html.Html a -- not a record 

Anstatt zu versuchen, Html msg -> Html msg zu verwandeln, sollten Sie so etwas wie versuchen:

-- 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 
+0

Ich vermutete, dass Ausgangs Javascript war. Meine "Html msg" kommt von der Ausgabe von 'Markdown.toHtml' also habe ich keine gute Möglichkeit, es zu manipulieren, bevor es als 'Html msg' von' Markdown.toHtml' zurückgegeben wird. Schade ist es nicht möglich, die Ausgabe zu transformieren. –

1

In Elm, Html a ist wirklich nur nützlich als Ausgabe. Sie werden es nie als Eingabe verwenden, wie es Ihre transform-Funktion versucht.

Sie werden besser bedient, indem Sie ein Modell erstellen, um Ihre Domäne zu beschreiben, und dann an eine view-Funktion übergeben, um HTML zu rendern.

type alias Article = 
    { priority : Priority 
    , headline : String 
    , body : String 
    } 

type alias Model = 
    List Article 

type Priority = First | Second 

Ihre Ansicht könnte dann wie folgt aussehen:

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 ] 
     ] 
+0

Wie ich zu halfzebra kommentierte, habe ich Markdown-Inhalt, den ich mit 'Markdown.toHtml' in' Html msg' transformiere. Ich möchte den Link in meinem Markdown-Inhalt nicht haben und kann anscheinend nicht die Ausgabe von 'Markdown.toHtml' transformieren, um den Link hinzuzufügen. Das ist eine Schande. –

+0

Gefallen. Ich würde empfehlen, die Markdown-Bibliothek zu erweitern, aber anscheinend ist das nur ein Wrapper um die JavaScript-Bibliothek https://github.com/chjj/marked –

+0

@Mikael Lundin Können Sie Ihren Markdown programmatisch erweitern, bevor Sie ihn an Markdown.toHtml übergeben? –