Comments (10)
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.
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.
Hey Dan, closing this. Ended up not using the innerHTML enough to warrant a shorthand. Cheers! :-)
from reagent.
For the record, @mpcarolin has done a pretty neat lib for that very purpose: https://github.com/mpcarolin/markdown-to-hiccup
from reagent.
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.
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:
- A page may contain hundreds of translated text snippets, meaning potentially hundreds of additional tags. Haven't tested the perf impact of this yet.
- 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.
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.
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.
@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.
@Deraen Thank you! It does not really fit my usecase. But good to know!
from reagent.
Related Issues (20)
- Infinite loop in the reagent.impl.input wrapper HOT 1
- Reaction value propagation glitch - the dirty flag is not set HOT 3
- Bad react key warning when using false as key
- examples/material-ui not work HOT 1
- examples/material-ui not work HOT 1
- Docs on โChanged?โ inaccurate? HOT 5
- React 18 does not live reload with Shadow-CLJS in non-trivial projects HOT 5
- Prop types
- `TypeError` when using function components with `r/create-class` with `dom.server/render-to-string`
- ReactDOM.render is no longer supported in React 18. Use createRoot instead. HOT 2
- shadow-cljs emits a warning when using with-let HOT 4
- Korean input broken on :input and :textarea HOT 1
- Update docs? HOT 1
- MUI ThemeProvider not working HOT 1
- @@ -62,6 +62,45 @@ class _Config { } } // eslint-disable-next-line valid-jsdoc /** * fetch problem title, level via solved.ac api * @see {@link https://solvedac.github.io/unofficial-documentation/#/operations/getProblemByIdArray} * @param {string[]} pids - list of problem id (up to 100) * @param { ({ problemId, titleKo, level }) => void } callback */ function fetchProblemsFromSolvedAc(pids, callback) { const query = encodeURIComponent(pids.join(',')); console.log(`https://solved.ac/api/v3/problem/lookup?problemIds=${query}`); fetch(`https://solved.ac/api/v3/problem/lookup?problemIds=${query}`) .then((res) => { console.log('fetchProblemsFromSolvedAc', res); return res; }) .then((res) => res.json()) .then(callback) .catch(() => callback(null)); }
- Readme says npm i or add deps to .edn but (in my case at least) needed both HOT 1
- Any interest in being able to replace `react/createElement` with a custom function? HOT 7
- The cursor disappears at the end of a controlled input on Chrome
- Reusable components following HTML semantics of Opional Attributes and Variadic Children HOT 3
- Controlled input loses cursor under ShadowRoot in React 18 HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google โค๏ธ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from reagent.