9

This is my code:

<template>
    <div>      
      <div v-html="data"></div> <button v-on:click="replace">Click Me to replace div contents</button>
    </div>
</template>
<script>
export default {
  data() {
    return {
      data: "I will be replaced once you click on button"
    }
  },
  methods: {
    clickMe() {
      alert("worked");
    },
    replace(){
      this.data = "Why does click me not work? It is loaded from server via ajax <a href v-on:click.prevent='clickMe'>Click Me</a>";
    }
  }
};
</script>

Here if I click on Click Me to replace div contents the content is replaced but the event handler clickMe does not fire. This data would come from server and I need to compile this string and use it from within the Vue's context so Vue can handle events etc.

How can I have the dynamic string downloaded from server work? I am using Vue 2.

Tim Liberty
  • 2,099
  • 4
  • 23
  • 39
  • What do you intend to see when you click on the button? – samayo Nov 09 '17 at 13:31
  • @samayo: Thanks for replying. When "Click Me to replace div contents" is clicked I would see `Why does click me not work? It is loaded from server via ajax Click Me` This is correct however now when I click on *Click Me* I do not see alert. That is my problem. – Tim Liberty Nov 09 '17 at 13:34

4 Answers4

15

Since v-html isn't compiled you will have to create a mini component like this to get around the issue:

new Vue({
  el: '#app',
  data () {
    return {
      data: ``
    }
  },
  
  computed: {
    compiledData () {
      return {
       template: `<p>${this.data}</p>`
      }
    }
  },
  
  methods: {
    replace () {
       this.data = `Now click on me <a href='#' @click.prevent='alert("yo")'> here </a>`
    }
  }
})
<script src="https://unpkg.com/vue@2.5.3/dist/vue.min.js"></script>


<div id="app">
  <component :is="compiledData" ></component>
  <button v-on:click="replace">Click Me to replace div contents</button>
</div>

The above code compiles the string content and thus you can run/execute the function as intended

bflemi3
  • 6,698
  • 20
  • 88
  • 155
samayo
  • 16,163
  • 12
  • 91
  • 106
5

Other solution using Vue components (codepen):

<script src="https://unpkg.com/vue"></script>
<div id="app">      
  <div id="someId"></div> <button v-on:click="replace">Click Me to replace div contents</button>
  <component :is="currentView"></component>
</div>

<script>
let app = new Vue({
  el: '#app',
  data: {
    currentView: null   
  },
  methods:{
    replace: function(){
      var templateFromServer = getTemplate();
       var comp=Vue.component('template-from-server', {
          template: templateFromServer,
          methods:{
            clickMe:function (){
              console.log("click");
            }            
          }
        });   
      this.currentView = comp;
    }   
  }
});

function getTemplate(){
  return "<a href v-on:click.prevent='clickMe'>Click Me</a>"
}
</script>
panicoper
  • 1,034
  • 1
  • 12
  • 15
0

v-html is not compiled as a Vue template. From the docs:

Note that the contents are inserted as plain HTML - they will not be compiled as Vue templates. If you find yourself trying to compose templates using v-html, try to rethink the solution by using components instead.

see: https://v2.vuejs.org/v2/api/#v-html

tony19
  • 125,647
  • 18
  • 229
  • 307
craig_h
  • 31,871
  • 6
  • 59
  • 68
-1

You can not render VueJS code from a html string.

You can solve this issue by using v-if

<div>
<div v-if="data">I will be replaced once you click on button</div> 
<div v-else>Why does click me not work? It is loaded from server via ajax <a href @click.prevent='clickMe'>Click Me</a></div>
<button @click="replace">Click Me to replace div contents</button>
</div>

<script>
export default {
  data() {
    return {
      data: true
    }
  },
  methods: {
    clickMe() {
      alert("worked");
    },
    replace(){
      this.data = !this.data;
    }
  }
};

You can call normal javascript function from string but not vuejs function so onclick event would also work.

Shubham Patel
  • 3,041
  • 24
  • 33
  • The full string is returned from the server along with @click so this solution will not work. – Tim Liberty Nov 09 '17 at 13:39
  • As mention is https://vuejs.org/v2/api/#v-html "The contents are inserted as plain HTML - they will not be compiled as Vue templates". You need to use components and pass the server returned string as a template. – Shubham Patel Nov 09 '17 at 13:49