Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VueJs manipulate inline template and reinitialize it

this question is similar to VueJS re-compile HTML in an inline-template component and also to How to make Vue js directive working in an appended html element

Unfortunately the solution in that question can't be used anymore for the current VueJS implementation as $compile was removed.

My use case is the following:

I have to use third party code which manipulates the page and fires an event afterwards. Now after that event was fired I would like to let VueJS know that it should reinitialize the current DOM.

(The third party which is written in pure javascript allows an user to add new widgets to a page)

https://jsfiddle.net/5y8c0u2k/

HTML

<div id="app">
    <my-input inline-template>
  <div class="wrapper">
  My inline template<br>
  <input v-model="value">
  <my-element inline-template :value="value">
    <button v-text="value" @click="click"></button>
  </my-element>
  </div>
    </my-input>
</div>

Javascript - VueJS 2.2

Vue.component('my-input', {
    data() {
        return {
            value: 1000
        };
    }
});

Vue.component('my-element', {
    props: {
        value: String
    },
    methods: {
        click() {
            console.log('Clicked the button');
        }
    }
});

new Vue({
    el: '#app',
});

// Pseudo code
setInterval(() => {
  // Third party library adds html:
  var newContent = document.createElement('div');
  newContent.innerHTML = `<my-element inline-template :value="value">
     <button v-text="value" @click="click"></button>
  </my-element>`;   document.querySelector('.wrapper').appendChild(newContent)

   //      
   // How would I now reinialize the app or
   // the wrapping component to use the click handler and value?
   //

}, 5000)
like image 744
jantimon Avatar asked May 05 '17 12:05

jantimon


2 Answers

After further investigation I reached out to the VueJs team and got the feedback that the following approach could be a valid solution:

/**
 * Content change handler
 */
function handleContentChange() {
  const inlineTemplates = document.querySelector('[inline-template]');
  for (var inlineTemplate of inlineTemplates) {
    processNewElement(inlineTemplate);
  }
}


/**
 * Tell vue to initialize a new element
 */
function processNewElement(element) {
  const vue = getClosestVueInstance(element);
  new Vue({
    el: element,
    data: vue.$data
  });
}

/**
 * Returns the __vue__ instance of the next element up the dom tree
 */
function getClosestVueInstance(element) {
  if (element) {
    return element.__vue__ || getClosestVueInstance(element.parentElement);
  }
}

You can try it in the following fiddle

like image 117
jantimon Avatar answered Nov 08 '22 22:11

jantimon


Generally when I hear questions like this, they seem to always be resolved by using some of Vue's more intimate and obscured inner beauty :)

I have used quite a few third party libs that 'insist on owning the data', which they use to modify the DOM - but if you can use these events, you can proxy the changes to a Vue owned object - or, if you can't have a vue-owned object, you can observe an independent data structure through computed properties.

window.someObjectINeedtoObserve = {...}

yourLib.on('someEvent', (data) => {
  // affect someObjectINeedtoObserve...
})

new Vue ({
  // ...
  computed: {
    myObject () {
      // object now observed and bound and the dom will react to changes
      return window.someObjectINeedtoObserve 
    }
  }
})

If you could clarify the use case and libraries, we might be able to help more.

like image 1
Tremendus Apps Avatar answered Nov 08 '22 23:11

Tremendus Apps