How to compile template loaded from API in Vue.js

We recently had an unusual task at NubiSoft where we had to fetch the Vue.js component template from a database. This could be an easy task if it were plain HTML. It is enough then to use the v-html directive, which allows us to dynamically render any part of HTML code.

Imagine that we have a GET endpoint that returns an object with template and dataset with customers:

{
    "template": "<el-table :data='datasets' stripe>
        <el-table-column prop='firstName' label='First name' sortable></el-table-column>
        <el-table-column prop='lastName' label='Last name'></el-table-column>
        <el-table-column prop='email' label='e-mail'></el-table-column>
    </el-table>",
    "datasets": [
        {"firstName": "John", "lastName": "Doe", "email": "johndoe@email.com"},
        {"firstName": "Mark", "lastName": "Spencer", "email": "markspencer@email.com"}
    ]
  }

As you can see the template is datagrid from the element-ui library with columns definitions. Datasource is a simple list of customers.

The required step to present that table on our application is to add to the built client the template compiler by defining alias in the configuration file as it is mentioned in the official documentation: https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only

Example for webpack:

module.exports = {
  // ...
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
    }
  }
}

The compiler in the standard approach is required only during the build phase when it is transpiling the template to javascript. In our case, we need it also in runtime to process a template from API.

In the last step, we can prepare a dedicated component to fetch the data and render that datagrid by using Vue.component():

<template>
    <component :is="customersTemplate" :datasets="datasets"></component>
</template>

<script>
import Vue from 'vue';
import { mapGetters } from 'vuex';

import { generateTable } from '@/api/customers';

export default {
  name: 'Customers',
  data() {
    return {
      datasets: [],
      customersTemplate: null,
    };
  },
  computed: {
    reportTemplate() {
      return Vue.component('report-table', {
        props: {
          datasets: Array,
        },
        template: this.customersTemplate,
      });
    },
  },
  async created() {
    await this.fetchReport();
  },
  methods: {
    async fetchReport() {
      const table = await generateTable().then((response) => {
        if (response.ok) {
          return response.data;
        }
      });
      this.datasets = table.datasets;
      this.customersTemplate = table.template;
    },
  },
};
</script>

Finally, we have a completely dynamic component that is database-driven and our customers list can be updated without rebuilding and deploying the frontend part of the application.

Leave a Reply

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