Hi,
I have some data that contains styling (strong, italic, etc) and I can't find a way to escape the html so it is properly rendered when I embed it in the template. Example
<custom-tag>
<p>{expression_with_html}</p>
this.expression_with_html = "Hello <strong>world</strong>"
</custom-tag>
Will output Hello <strong>world</strong> instead of "Hello world"
Would be much appreciated if anyone had a tip on this side!
Thanks.
The above will not be supported because it's vulnerable to XSS.
You need to have two separate tokens for the string. For example:
<p>{ hello[0] }<strong>{ hello[1] }</string></p>
this.hello = ['Hello', 'world']
Hey, thanks for the quick answer!
I clearly understand that, but I the data is coming directly from de DB. Pre-processing it before to separate into multiple tag would be a pain I would want to skip.
Similar cases are often handle with a "raw" filter to explicitely trust the incoming source as valid HTML..
How would you see it possible to introduce it as a core mechanic abstracted from the client that would allow only pre-compiled indicated "raw" content to be evaluated as HTML.
I took a peek in the code but I'm in the middle of a production push (I turned around Friday claiming we were trying out Riot directly in production, delivering the new needed feature with it).
I see this as an issue as it can be a frequent case to handle already formatted HTML and all I see as workarounds are horrible hacks.
I'll look into the problem and report my findings.
Thanks again and great work with Riot, up to now I love it!
I see your concern and I can see this coming from elsewhere too.
Maybe there is a great solution to this but I cannot promise anything official soon.
You can fix your issue with a custom Riot by changing this line from:
if (!attr_name) return dom.nodeValue = value
to
if (!attr_name) return dom.innerHTML = value
Ha!. I knew it was coming from some simple dom processing somewhere around there. Couldn't find it right away.
I'll gladly apply that patch for now and I will also look into it, see if I can figure a solution that is secure and flexible.
Grateful thanks, this is a most welcomed help, given my circumstances!
Glad to help. No need for patches at this point. I first want to see if this is a common concern.
I first want to see if this is a common concern.
This functionality would be appreciated, my use case is to display data from a rich text editor.
Just wondering, how much of a hack is doing something like this:
riot.tag('raw', '<span></span>', function(opts) {
this.root.innerHTML = opts.r;
});
riot.mount('raw', {
r: 'Some <b>raw</b> HTML'
})
That is a pretty elegant solution! I've used it like so, to allow for updates
riot.tag('raw', '<span></span>', function (opts) {
this.updateContent = function () {
this.root.innerHTML = opts.content;
};
this.on('update', function() {
this.updateContent();
});
this.updateContent();
});
And the tag definition is used as so : <raw content="{someHtml}"></raw>
I don't see any major downsides. It is simple, reusable and as far as I can see, protected against XSS as long as you use it on trusted inputs (i.e. not client facing)
@tipiirai Would you see a solution like that make it into the the core?
That would even raise the question of how relevant it could be to have a public repository of reusable custom tags..!
Definitely, Riot is making it's way up in my toolkit at a lightning speed!
This is a nice trick indeed! You are simply using the Riot API so nothing against it.
I think the use case of inserting HTML with riot will be a use case which which happen more often. Like react (which has dangerouslySetInnerHTML={{__html: rawMarkup}}), you can make it possible, but with a kinda warning:
<p>!{ content }</p>
Or
<p>!!!{ content }</p>
Or even
<p>!unsescaped{ content }</p>
It would be easy for ppl who know how to sanitize html and for new users you make it "clear" it is a dangerous action.
riot.tag('raw', '', function(opts) {
this.root.innerHTML = opts.html;
});
<raw></raw>
<raw html="<strong>I am strong</strong>"></raw>
riot.mount('raw', {
html: '<strong>I am strong</strong>'
});
I would love it if this were in core. It's useful for rich test editors, markdown previews, etc. Adding the tag isn't bad, but it seems this is a fairly common use case. If you disagree, then I guess adding a custom tag isn't so bad :)
This along with inner-html will be the first additions to the "riot standard" library to be introduced later. It will not be part of core but an optional library that you can include. See https://github.com/muut/riotjs/issues/300#issuecomment-74371056
+1 for core functionality, though a standard library is acceptable too. @ufologist thanks for that snippet!
in dust this is simply {someHTMLContent | s} and in handlebars it's {{{someHTMLContent}}} it's a much cleaner way to handle this
Got an issue here, if I use the tag like this:
<raw html="<strong>I am strong</strong>"></raw>
And I have a big content (compiled markdown article) need to render, because the content is an html attribute, it almost doubled the size of html.
Any idea about it? (I'm using Riot 2.3)
And I have a big content (compiled markdown article) need to render, because the content is an html attribute, it almost doubled the size of html
And what's the problem? This content is not loaded but it's created in runtime so it does not affect your app performances
Another solution can be simple this.nameOfTheParent.innerHTML = '<my><markdown><html>...'
@GianlucaGuarini Thanks for your reply, but what about the server rendering?
It should work in the same way
Got this exception when I use this.wrapper.innerHTML = data on server rendering:
SyntaxError: Unexpected token ...
at Function (native)
at _create (/Users/leozdgao/workspace/tryRiot/node_modules/riot/riot.js:735:12)
at _tmpl (/Users/leozdgao/workspace/tryRiot/node_modules/riot/riot.js:707:43)
at /Users/leozdgao/workspace/tryRiot/node_modules/riot/riot.js:1690:15
at each (/Users/leozdgao/workspace/tryRiot/node_modules/riot/riot.js:1793:23)
at update (/Users/leozdgao/workspace/tryRiot/node_modules/riot/riot.js:1686:3)
at Tag.<anonymous> (/Users/leozdgao/workspace/tryRiot/node_modules/riot/riot.js:1452:5)
at Tag.<anonymous> (/Users/leozdgao/workspace/tryRiot/node_modules/riot/riot.js:1510:38)
at mountTo (/Users/leozdgao/workspace/tryRiot/node_modules/riot/riot.js:2236:9)
at pushTags (/Users/leozdgao/workspace/tryRiot/node_modules/riot/riot.js:2347:17)
It seems riot failed to parse my content for some special character?
But if i use <raw html={opts.data.content}></raw>, it works... It confused me.
@leozdgao can you open a new issue posting your use case please?
nice solution, thanks!
I'd love to have
@Mikechaos I am trying to follow your suggestion because I want to update the html content of the raw tag. But I am unable to do that. Could you increment your example including the usage code in the parent tag?
I am trying to call self.update() manually without success. http://riotjs.com/api/#events
@mgenev It _should_ be difficult to insert raw HTML, to prevent people from doing it without understanding the consequences. A "simpler" way for doing this isn't necessarily desirable, and I'd strongly argue _against_ syntax like that of Dust or Handlebars, for precisely this reason.
i'm trying to modify an existing riot-gear tag
rg-tags main loop:
<li each="{options}"....>
<raw r={text}></raw>
</li>
riot.tag('raw', '<span></span>', function(opts) {
this.root.innerHTML = this.boldMatchedText(opts.r);
});
Is there a cleaner/faster way to do this or should I just bold the characters the old fashioned way with Jquery, on tag update? Note: removing the raw tag and calling my function {boldMatchedText(text)} works great (except for the escaped html)
The problem with that custom tag is that it doesn't refresh the content whenever the parent tag is updated with new content...
It would be great to see such feature in Riot v3. {* unescapedHtml } might be a nice syntax ;)
I would suggest making it a more difficult and obviously-dangerous syntax/API, similar to what React does.
+1
I used to use React's dangerouslySetInnerHTML for displaying converted markdown text (which was secure, coming from backend only).
Using this marked js library which provides the method marked to convert markdown to html, you can then do the following (which also updates automatically):
riot.tag('markup', '', function(opts) {
this.on('update', function() {
let markup = opts.data;
if (!markup) return;
let html = marked(markup);
this.root.innerHTML = html;
})
});
<markup data={myMarkup}></markup>
or in jade/pug:
markup(data='{myMarkup}')
based on @ufologist work I've found it won't work on updates, so this version should work:
riot.tag('raw', '<div></div>', function(opts) {
this.on('update', function() {
this.root.childNodes[0].innerHTML = opts.html;
}.bind(this))
})
How to make it work with attributes? Consider our below (simplified) scenario:
<tag-panel>
<h2>{{opts.title}}</h2>
<yield/>
</tag-panel>
<tag-searchresults>
<tag-panel title="Results: <small>{searchText}</small>"> </tag-panel>
this.searchText = "something retrieved from input text box";
</tag-searchresults>
on riot 3.x the update event isn't fired on mount :disappointed: . Had to change it to:
riot.tag('raw', '<div></div>', function(opts) {
this.set = () => { this.root.childNodes[0].innerHTML = opts.html }
this.on('update', this.set)
this.on('mount', this.set)
})
btw @GianlucaGuarini, can't see that breaking change on the list of http://riotjs.com/release-notes/#300-november-22-2016
also noticed the object iteration changed from each={k,v in object} to each={v,k in object}
also noticed the object iteration changed from each={k,v in object} to each={v,k in object}
Change: different approaches in iterable objects in different contexts of “each - in” riot/1420 (breaking change)
When setting this.root.innerHTML to some html that contains _custom riot tags_, how do I get riot to re-parse the injected html and mount the custom tags?
<raw>
<script>
// custom tags are parsed and mounted
// when the 'raw' component is first mounted
this.root.innerHTML = opts.html
// ...however when setting innerHTML after an update
// any custom tags are not parsed and mounted
this.on('update', () => {
this.root.innerHTML = opts.html
})
</script>
</raw>
// contrived custom tag...
<image>
<img src="{opts.src}"/>
</image>
<app>
<raw html="{html}"/>
<script>
// this works...
this.html = '<image src="cat.jpg">'
setTimeout(() => {
// this does not work :(
// the update event in the <raw> tag is fired and the innerHTML is correctly set
// ...but the custom <image> tag is not parsed and mounted by riot
this.update({
html: '<image src="dog.jpg">'
})
}, 1000)
</script>
</app>
The only 'work around' (hack) I have arrived at is calling this.unmount(true) in the 'update' callback within the <raw> component immediately followed by this.mount:
<raw>
<script>
this.root.innerHTML = opts.html
this.on('update', () => {
// no need to set innerHTML since calling unmount followed by mount
// causes the tag to be re-run and the innerHTML is parsed and mounted
this.unmount(true)
this.mount()
})
</script>
</marked>
Though the above solution works, it feels like a real hack, so I was wondering if there was another method/approach?
This works for me
<raw>
<!-- RAW HTML into DOM -->
<script>
var set = () => {
this.root.innerHTML = opts && opts.html
}
this.on('mount', set )
this.on('update', set )
this.parent.on('update', set )
</script>
</raw>
Shortest for incoming data used within each tag loop
<raw>
<span></span>
<script>
this.on('mount', () => {
this.root.innerHTML = opts.content
});
</script>
</raw>
Most helpful comment
This functionality would be appreciated, my use case is to display data from a rich text editor.