hzhu
hzhu

Reputation: 3799

How to convert HTML tag with style to Hiccup? React problems

I'm trying to parse HTML with CSS into Hiccup in a Reagent project. I am using Hickory. When I parse HTML with inline CSS, React throws an exception.

      (map 
         as-hiccup (parse-fragment "<div style='color:red'>test</div>")
      ) 

The above generates [:div {:style color:red} "test"] & Reactjs returns exception from Reactjs:

Violation: The style prop expects a mapping from style properties to values, not a string.

I believe [:div {:style {"color" "red"}} "test"] must be returned instead.

Here is the code view:

(ns main.views.job
  (:require [reagent.core :as reagent :refer [atom]]
                    [hickory.core :refer [as-hiccup parse parse-fragment]]))

(enable-console-print!)

(defn some-view [uid]
  [:div
     (map as-hiccup (parse-fragment "<div style='color:red'>test</div>"))   
  ])

Upvotes: 3

Views: 2418

Answers (1)

sbensu
sbensu

Reputation: 1521

The whole repo is here and it works. I added the parsing from style tag to a map for React in the core.cljs file:

(ns hickory-stack.core
  (:require [clojure.string :as s]
            [clojure.walk :as w]
            [reagent.core :as reagent :refer [atom]]
            [hickory.core :as h]))

(enable-console-print!)

(defn string->tokens
  "Takes a string with syles and parses it into properties and value tokens"
  [style]
  {:pre [(string? style)]
   :post [(even? (count %))]}
  (->> (s/split style #";")
       (mapcat #(s/split % #":"))
       (map s/trim)))

(defn tokens->map
  "Takes a seq of tokens with the properties (even) and their values (odd)
   and returns a map of {properties values}"
  [tokens]
  {:pre [(even? (count tokens))]
   :post [(map? %)]}
  (zipmap (keep-indexed #(if (even? %1) %2) tokens)
          (keep-indexed #(if (odd? %1) %2) tokens)))

(defn style->map
  "Takes an inline style attribute stirng and converts it to a React Style map"
  [style]
  (tokens->map (string->tokens style)))

(defn hiccup->sablono
  "Transforms a style inline attribute into a style map for React"
  [coll]
  (w/postwalk
   (fn [x]
     (if (map? x)
       (update-in x [:style] style->map)
       x))
   coll))

;; Test Data

(def good-style "color:red;background:black; font-style: normal    ;font-size : 20px")

(def html-fragment
  (str "<div style='" good-style "'><div id='a' class='btn' style='font-size:30px;color:white'>test1</div>test2</div>"))

;; Rendering

(defn some-view []
  [:div (hiccup->sablono
         (first (map h/as-hiccup (h/parse-fragment html-fragment))))])

(reagent/render-component [some-view]
                          (. js/document (getElementById "app")))

Upvotes: 2

Related Questions