Vue js render text with html content

6.7k Views Asked by At

What i'm trying to do is have a root element with a prop that holds inner html like: hello<b>hey</b> but i can't use v-html because this element also has children for example:

<template>
  <component :is="element.tag" contenteditable="true">
    <div contenteditable="false">
      <span class="delete-obj" :id="'delete'+element.id" >delete</span>
    </div>
    <RenderString :string="element.content" />
  </component>
</template>
<script>
    import Vue from "vue";
  
    Vue.component("RenderString", {
        props: {
            string: {
                required: true,
                type: String
            }
        },
        render(h) {
            const render = {
                template:  this.string ,
                methods: {
                    markComplete() {
                        console.log('the method called')
                    }
                }
            }
            return h(render)
        }
    })
    export default {
        name: "VElement",
        props: {
            element: {
                required: false,
                default: null
            },
        },
    }
 </script>

I have tried the above and I have tried using slots. I can solve it with vanilla JavaScript like element.innerText, but I don't want to. The main goal is that the element is editable when they type they are editing element.content that will be rendered and the div that's inside it is normal HTML that I also need. The main problem is that the inner HTML that I want doesn't have a root element. The element is something like:

{id:1,tag:"div",content:"hello<b>hey</b>"}

I want the final result to be:

<div contenteditable="true">
    <div contenteditable="false">
        <span class="delete-obj" :id="'delete'+element.id" >delete</span>
    </div>
    hello<b>hey</b>
<div>

And I want to edit hello<b>hey</b> when I click inside I don't want it wrapped in anything else and if I put v-html on the outer div the inner div is gone.

2

There are 2 best solutions below

1
On

Like Seblor said, v-html will work with nested html strings.

Simply replacing the RenderString component with a <div v-html="element.content"/> should get you what you want.

Tested with the given example of hello<b>hey</b>:

Vue.component('VElement', {
  name: "VElement",
  props: {
    element: {
      required: false,
      default: null
    },
  },
  template: '\
    <component :is="element.tag" contenteditable="true">\
      <div contenteditable="false">\
        <span class="delete-obj" :id="\'delete\'+element.id">delete</span>\
      </div>\
      <div v-html="element.content"/>\
    </component>'
})

new Vue({
  el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <v-element :element="{id:1, tag:'div', content:'hello<b>hey</b>'}" />
</div>

0
On

If you really aren't able to wrap the content in a parent element, maybe a good approach is to write a VUE directive that render the text.

JS FIDDLE FULL DEMO

//DIRECTIVE
Vue.directive('string', {
    inserted(el, bind) {
        /** 
        * Here you can manipulate the element as you need.
        */

        el.insertAdjacentText('beforeend', bind.value); 
    }
});

//TEMPLATE
<template>
  <component :is="element.tag" contenteditable="true" v-string="element.content">
    <div contenteditable="false">
      <span>delete</span>
    </div>
  </component>
</template>