Anshul
Anshul

Reputation: 395

Vue: Bind Change event to dynamically inserted content

I have a Vue app which get an html from API that contains some code like <div class="play-video"> <input type="text" class="input-1"/></div>

Calling the API with axios via a promise, it is inserted into the dom something like:

<div v-if="content" v-html="content"></div>

How can I bind on change events to the children inputs with the .input-1 class?

Upvotes: 1

Views: 1799

Answers (2)

AlexMA
AlexMA

Reputation: 10192

There are two approaches that rise to the top for me:

  1. From inside a vue app, use vanilla javascript to append those nodes to the DOM and then query for their inputs and add event handlers. Since you're inside the Vue app you can write the handlers to interact with Vue components. Or you could probably do something fancy with vuex getters returning those elements (if they're still attached to the document).
  2. Use Vue.compile to create render functions from the html or add an async omponent. However, this will only work if you have a robust way to add @change="doSomething" to the template. For this reason, I'd probably should go with option 1) unless you control the source of the templates.

Whichever you do, keep malicious injections in mind.

const someHtml = '<div class="play-video"><input type="text" class="input-1"/></div>'

var app = new Vue({
  el: '#app',
  mounted() {
    const dynamicContent = document.createElement('div');
    dynamicContent.innerHTML = someHtml;
    dynamicContent.querySelector('input').addEventListener('change',
      e => this.inputValue = e.target.value);
    this.$refs.container.appendChild(dynamicContent);
  },
  data() {
    return {
      name: 'Vue',
      inputValue: ''
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div>type something below then click this text</div>
  <div ref="container"></div>
  <div>The user entered: {{inputValue}}</div>
</div>

Upvotes: 0

tony19
tony19

Reputation: 138226

You could query the container for those inputs, and add an event handler to each:

  1. Apply a template ref (named container) on the container div:

    <div ref="container">
    
  2. Add a watcher on content that queries the container (via this.$refs.container.querySelectorAll()) for <input class="input-1">, and adds an event handler to each input. Note that the handler needs to wait until $nextTick(), after which the v-html directive would have had a chance to update.

    export default {
      watch: {
        content: {
          async handler(content) {
            // wait til v-html directives takes effect
            await this.$nextTick()
    
            this.$refs.container
              .querySelectorAll('.input-1')
              .forEach((input) => input.addEventListener('change', this.onInputChange))
          },
          immediate: true,
        },
      },
      methods: {
        onInputChange(e) {
          console.log('input change')
        },
      },
    }
    

demo

Upvotes: 3

Related Questions