Categories javascript

Workaround for DOM manipulation in Vue app

Post date March 11, 2021

I like writing javascript in a progressive enhancement manner – I assume there is some HTML (DOM) to work with and build my javascript magic around it. Autocomplete or validation for an input? Just grab that input and add event listeners to make users’ lives easier. Pagination without reloading the whole page? Intercept the click on fully functioning link and load data per AJAX (and adjust the browser history with History API to make browser navigation work as expected). Want to have that code snippet with syntax highlighting? No problem, just give me the DOM node to parse. But that’s all old school. The new school – the javascript framework school – does not work very well with my old school scripts, because the DOM is just not there to be parsed and manipulated. But the scripts are still good and a few adjustments are sometimes enough. Let’s have a look at how to use old school scripts with Vue.js framework.

Disclaimer: I’m not a Vue expert. At least not yet. This article is more like “today I’ve learnt” kind of thing to help myself in the future or other beginners to get acquainted with some mechanisms Vue has to offer.

The mounted lifecycle hook

If the old school script needs to parse and adapt the DOM only once (syntax highlighting for code snippets for example), then you should try mounted lifecycle hook. The code will be executed when the DOM is ready and the script should work just like it would with HTML delivered from the server. Example code:

new Vue({
  el: '#app',
  mounted() {
    //here comes the old school code, e.g.
    //new Widget('.fancy-class');
  }
});

As the documentation notes: sometimes your component has components nested inside it. If you want to make sure all children of the component are also rendered before calling your custom code use $nextTick like this:

new Vue({
  el: '#app',
  mounted: function () { 
    this.$nextTick(function() { 
      // Code that will run only after the 
      // entire view has been rendered
    }) 
  } 
});

If some event listeners get attached in the process, you should remember to clean up before the Vue component gets removed. Use beforeDestroy lifecycle hook for that:

new Vue({
  el: '#app',
  data:{
    widget: null
  },
  mounted() { 
    //here comes the old school code, e.g. 
    this.widget = new Widget('.fancy-class'); 
  },
  beforeDestroy() {
    //clean up before the Vue component gets removed
    this.widget.destroy();
  } 
});

The computed property

If your old school code takes text input and produces text output (for example generating HTML to preview markdown text), you may want to use computed property. Assuming your input is passed in raw and the output is the result, the code would look like this:

new Vue({
  el: '#app',
  data:{
    raw: ''
  },
  computed: {
    result() {
      //do the magic here and return
      return myOldSchoolHelper(this.raw);
    }
  } 
});

The filter option

Similarly to the computed property you can modify the output by using a filter.

If you need the feature in many different places in your application it may be a good idea to go with filter as it will be available globally for all the children components. To define a filter use this snippet of code:

Vue.filter('reverse', function (value) {
  //do the magic here and return
  return myOldSchoolHelper(value);
});

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

And the template (“HTML”) code would look like this:

<div id="app">
  <p>{{ message | reverse }}</p>
</div>

A custom method

Sometimes you don’t have the value you want to modify in the props or data of your component, so the previous techniques would not work as you cannot reference the initial value with this.raw or similarly. You probably could create a nested component and useĀ  props there, but there is another technique that you might find useful – using a generic method. Have a look at the following code:

new Vue({
  el: '#app',
    data: { 
      items: ['a', 'b', 'c']
    },
  methods: { 
    enhance(rawData) { 
      //do the magic here and return
      return myOldSchoolHelper(rawData); 
    } 
  }
});

And the template:

<div id="app">
  <p v-for="item in items">{{ enhance(item) }}</p>
</div>

Conclusion

There are many ways to integrate older scripts in Vue.js. We have life cycle hooks, computed properties, filters and custom methods in our toolbox. Of course migrating the logic to a Vue plugin would be great (and probably the preferred way), but if you need the result quickly, the techniques presented above should be useful for you.

Leave a Reply

Your email address will not be published. Required fields are marked *