Closed pxwee5 closed 6 years ago
This doesn't play well with the virtual dom/ render function approach we have ya pen Vue 2.0.
It would require major changes to underlying architecture, which is something we likely don't want to do at this point, in my opinion.
Out of curiosity: what kind of app are you building that relies heavily on inserting html like that?
In my perception this is an escape hatch for some edge cars but should not be something one replies on regularly. But that's just my experience.
I'm using prismic headless CMS's prismic-dom library. It returns field values in HTML. Of course the above is just an example that I use to illustrate how we can make use of the triple curly braces.
prismic-dom returns the field values from the CMS as HTML string. My frontend components will structure the values in such a way. e.g.
<div>
<div v-html="titleHTML"></div>
<div v-html="imageHTML"></div>
<div v-html="contentHTML"></div>
</div>
This is just a basic example but you can see that all the wrapping divs are actually redundant. It makes the title, image and content hard to style because each containing element becomes independent and you can't do h2 + img
or img + p
.
I'd love to know if there's a better way to approach this though.
Why not joining all the potential HTML together?
<div v-html="titleHTML + imageHTML + contentHTML"></div>
Maybe you can make v-html
work with <template>
tags?
No, v-html
won't work on <template>
A (admittedly hacky and less performant) way would be to render Vue instances with each pievce of HTML, extract the vnodes, and use a dnymaically created functional component to display them:
https://jsfiddle.net/Linusborg/mfqjk5hm/
Ugly, but works :D
Edit: updated fiddle to be more reusable.
Thanks LinusBorg and Akryum. Both of you are really helpful. I think for now I will go with Akryum's solution and see where that takes me. It definitely works with the example shown above.
I'll report back if it gets tricky in a more sophisticated use case.
i cant render thread body with html with vue inline template thread body has {{ somecontent }}
this is the laravel blade where i am putting my all html from database {!! $thread->body !!}
error is
ReferenceError: somecontent is not defined
As Akryum suggested you can use v-html="titleHTML + imageHTML + contentHTML"
to concat the HTML elements and and use ${variableToRender}
to use component rendered elements, so for @bhushangaykawad issue, you should be able to do something like
<div v-html="variableWhatever + `<img src='${require('~/assets/whatever-asset')}'/>` + $t('translated.content.one')">
Why not joining all the potential HTML together?
<div v-html="titleHTML + imageHTML + contentHTML"></div>
This solves it - IF you just have a block of CMS stuff - but in many cases I still have different data coming in that need to adhere to adjacent selector rules: https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator - and the first element inside the v-html
element needs to be directly next to the one before it.
I put a related topic in the forum: https://forum.vuejs.org/t/raw-html-without-a-parent-element-via-v-html/87160
I'm excited to try @LinusBorg 's solution. Seems like that'll give me what I need. The content in my project is complex - the performance shouldn't matter that much. It's just a complicated article / style-wise. : ) update - ^ solution worked great!
Hi!
Since I didn't find any ready-to-use solution, I took @LinusBorg's fiddle and wrapped it within a tiny Vue component - <VHTML>
. I hope someone will find it helpful 🙂
Here is my two cents take on a (Vue 3) component using directives to unwrap content.
Please note! createContextualFragment allows script execution* - why you would probably want to add some sort of sanitation
import { defineComponent } from 'vue';
//
// v-html component
// <v-html :html="HTML_STRING" />
//
export default defineComponent({
name: 'VHtml',
props: {
html: {
type: String,
required: true
}
},
directives: {
swap: {
mounted(el, binding) {
// createContextualFragment allows script execution
// why you would probably want to sanitize the html
// e.g. using https://github.com/cure53/DOMPurify
let safe = DOMPurify.sanitize(binding.value)
let frag = document.createRange()
.createContextualFragment(safe)
el.replaceWith(frag)
}
}
},
template: '<div v-swap="html"></div>'
})
*
const script = '<script>alert("You may not want this")<\/script>'
<v-html :html="script" />
This doesn't play well with the virtual dom/ render function approach we have ya pen Vue 2.0.
It would require major changes to underlying architecture, which is something we likely don't want to do at this point, in my opinion.
Out of curiosity: what kind of app are you building that relies heavily on inserting html like that?
In my perception this is an escape hatch for some edge cars but should not be something one replies on regularly. But that's just my experience.
The fact that the Vue project prioritize how convenient markup is for your internal implementation over having nicer-looking templates says a lot, and really makes me want to use React in the future.
Here's an example of where it would be helpful to have {{{ }}} or similar:
<div class="mt-2 pt-2">
{{ __('<span class="font-bold">Content Items</span> is an all-encompassing term to define WordPress posts, pages and other content that is added to a <span class="font-semibold italic">Section</span>.') }}
</div>
__() is an i18n function, in this example. I believe the only way to make this work, would be to declare this entire line as a variable so that v-html could make use of it - which is a bit of a pain.
What problem does this feature solve?
At the moment, whenever we use v-html, we are introducing another wrapping element on top of the element that we want to include in the template.
For eg.
<div v-html="paragraph"></div>
will generate the following code:However if we Inline these paragraphs as such:
This has introduced too many unnecessary wrapper elements surrounding our
<p>
element. And we lose the ability to style the paragraphs (in different colours and whatnot) with cssnth-child
Not only this, the paragraph margin that we set via
p + p { margin-top: 10px }
will fail as well because they stopped having relationship as soon as the divs are introduced. PS: The reason I used p + p is so that margin is applied in between the set of paragraphs only and not before or after.What does the proposed API look like?
Ideally the usage would be like this:
and should generate the following:
Result is much cleaner, highly intuitive and easy to read. Most importantly, we regain the ability to style the paragraphs anyway we want with this method.