GithubHelp home page GithubHelp logo

Comments (10)

holmsand avatar holmsand commented on May 4, 2024 2

Aaah, I see.

I'm a little bit reluctant to make shooting yourself in the foot prettier :) It is cumbersome in React to set innerhtml for a rather good reason, I think. But I'll definitely have another think about it.

Btw here's another way of adding the danger, that may or may not be prettier:

(defn dangerous
  ([comp content]
     (dangerous comp nil content))
  ([comp props content]
     [comp (assoc props :dangerouslySetInnerHTML {:__html content})]))

used as

   (dangerous :div "<i>italics</i>")
   (dangerous :div {:class 'fooclass} "<i>italics</i>")

But I'm very interested in making things like translation more convenient. For example by having more convenient way of manipulating the "hiccup structure" (must think of a better name for that, btw).

I've been thinking that something like a (replace-all elems fn), where fn would be called with every child element in elems, possibly returning modified versions of them, might be convenient. That should make implementing for example translation based on meta-data very convenient, but at the same time keep the core very simple.

Suggestions and thoughts more than welcome!

from reagent.

holmsand avatar holmsand commented on May 4, 2024 1

I think you should be able to use React's dangerouslySetInnerHTML directly, as in

[:div {:dangerouslySetInnerHTML {:__html "<b>I am bold</b>"}}]

It's not very nice looking, but I guess that is the point :)

from reagent.

ptaoussanis avatar ptaoussanis commented on May 4, 2024 1

Hey Dan, closing this. Ended up not using the innerHTML enough to warrant a shorthand. Cheers! :-)

from reagent.

piotr-yuxuan avatar piotr-yuxuan commented on May 4, 2024 1

For the record, @mpcarolin has done a pretty neat lib for that very purpose: https://github.com/mpcarolin/markdown-to-hiccup

from reagent.

ptaoussanis avatar ptaoussanis commented on May 4, 2024

Yes, sorry - was wondering if you've given any thought to exposing a more convenient way of doing the same.

So currently I have:

(defn html-div [html-content]                                                                                                                                                 
  [:div {"dangerouslySetInnerHTML"                                                                                                                                              
         #js{:__html html-content}}])                                                                                                                                           

(defn html-span [html-content]                                                                                                                                                
  [:span {"dangerouslySetInnerHTML"                                                                                                                                             
         #js{:__html html-content}}])

But it occurs to me that it might be interesting to do the same via element metadata instead, like ^:html [:div "foo"] or ^:unsafe [:div "foo"].

There are a couple common use cases for this, the most common (for me anyway) is when dealing with i18n translations that can carry locale-specific styling that's difficult to anticipate. I'll normally allow translators to use Markdown like "Buy **widget** now" that'll be transformed to an HTML string.

Tower takes this strategy and I'm busy porting it to ClojureScript so experimenting with possible ways of working cleaning with Reagent out-the-box.

from reagent.

ptaoussanis avatar ptaoussanis commented on May 4, 2024

I'm a little bit reluctant to make shooting yourself in the foot prettier :) It is cumbersome in React to set innerhtml for a rather good reason, I think. But I'll definitely have another think about it.

My 2c: I think having the default be safe is reasonable + sufficient. So long as the unsafe option is something you need to specifically reach for, I wouldn't have a problem making it more convenient (esp. if it's actually called "^:unsafe").

Would only really suggest this if it's a relatively common+legitimate use-case though (which I'm still not sure about). Will continue thinking about it myself, was just curious to get your thoughts (thanks btw!).

"hiccup structure" (must think of a better name for that, btw)

Hiccup form(s) maybe?

But I'm very interested in making things like translation more convenient.

The simplest way of getting Tower to work with React seems to be to just wrap each translation output in a "dangerous" tag to get React to respect any inline styles.

This has two disadvantages from what I can see:

  1. A page may contain hundreds of translated text snippets, meaning potentially hundreds of additional tags. Haven't tested the perf impact of this yet.
  2. Tower can produce both inline and block-level translations. That's going to complicate the wrapping a little since I'm going to need to be careful about the wrapper's tag type (span vs div).

I've been thinking that something like a (replace-all elems fn), where fn would be called with every child element in elems, possibly returning modified versions of them, might be convenient. That should make implementing for example translation based on meta-data very convenient, but at the same time keep the core very simple.

That's sounds interesting. Yeah, the fact that Reagent's dealing with raw Clojure data structures may open up some worthwhile possibilities. I'll think about that as well. One idea might be to render Tower translations to Hiccup forms rather than HTML strings. Will try come back to you if I come to any conclusions on my end.

from reagent.

holmsand avatar holmsand commented on May 4, 2024

I like the sound of "Hiccup forms".

And I also think there's a lot to be said for keeping output as Hiccup forms as long as possible.

I've been toying a bit with my "deep Hiccup forms replace" idea, and I feel quite confident that it could be very useful. Suppose you have something like this:

(defn- fast-mapv
  [f coll]
  (let [res (reduce-kv
             (fn [c k v]
               (let [v' (f v)]
                 (if-not (identical? v' v)
                   (assoc! (if (nil? c) (transient coll) c)
                           k v'))))
             nil coll)]
    (if (nil? res) coll (persistent! res))))

(defn elem-map
  "Return the result of applying f to each vector in elems,
including sub-vectors. Warning: uses recursion."
  [f elems]
  (letfn [(em [e]
            (if (vector? e)
              (fast-mapv em (f e))
              e))]
    (em elems)))

(defn children
  "Return the children part of a hiccup form (or nil if there aren't any)."
  [elem]
  (let [first-child (if (-> elem (get 1) map?) 2 1)]
    (if (> (count elem) first-child)
      (subvec elem first-child))))

(defn props
  "Return the props map of a hiccup form (or nil)."
  [elem]
  (if-let [p (get elem 1)]
    (if (map? p) p)))

Then you could do for example your prettification-of-foot-shooting like this:

(defn live-dangerously [elems]
  (elem-map (fn [e]
              (if (-> e meta :danger)
                [(e 0)
                 (assoc (props e)
                   :dangerouslySetInnerHTML
                   {:__html (-> e children first)})]
                e))
            elems))

(defn some-comp []
  (live-dangerously
   [:div
    [:p "Text in " ^:danger [:b "<i>italic</i>"]]]))

or run-time translations like this:

(def translations {:swedish {:hello "Hej"}})

(defn translate [lang elems]
  (elem-map (fn [e]
              (or
               (let [p (props e)]
                 (when-let [k (:translate p)]
                   (when-let [s (-> translations lang k)]
                     [(e 0) (props e) s])))
               e))
            elems))

(defn i18-comp []
  (translate :swedish
             [:div
              [:div
               "should be swedish"
               [:p
                [:span {:translate :hello} "Hello"]
                [:b [:span {:translate :hello} "Hello"]]]]]))

And you could do lots of other cool stuff (like augmenting some Hiccup-forms-like structure you get from the server, from a Markdown parser, etc.).

And I think performance should be good enough. On my machine the processing takes in the order of 1ms per 1000 vectors (and just a little bit more if there are many changes).

from reagent.

awb99 avatar awb99 commented on May 4, 2024

There is another aspect to this ticket that was not yet fixed: scripts that are added via this manner are never evaluated! I do work on a reagent based gorilla repl that needs to include components that bring their own Javascript inside html. How can that be solved?

from reagent.

Deraen avatar Deraen commented on May 4, 2024

@awb99 This is not limitation of Reagent or React, innerHTML not allowing script tags comes from HTML spec: https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML

Some ways to add/run script tags from components are to just manually modify document.body with normal DOM operations like appendChild, or runing JS code from img onload (or onerror).

from reagent.

awb99 avatar awb99 commented on May 4, 2024

@Deraen Thank you! It does not really fit my usecase. But good to know!

from reagent.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.