1

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?

2 Answers 2

3

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

Sign up to request clarification or add additional context in comments.

Comments

0

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.