Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic html elements in Vue.js

Tags:

How is it possible to add elements dynamically to the content? Example below:

<template>
    {{{ message | hashTags }}}
</template>

<script>
    export default {
        ...

        filters: {
            hashTags: function(value) {
                // Replace hash tags with links
                return value.replace(/#(\S*)/g, '<a v-on:click="someAction()">#$1</a>')
            }
        }
    }
</script>

Problem is that if I press the link no action will fire. Vue do not see new elements.

like image 670
user2058653 Avatar asked Sep 15 '16 17:09

user2058653


1 Answers

Update: Based on this answer, you can do a similar dynamic-template component in Vue 2. You can actually set up the component spec in the computed section and bind it using :is

var v = new Vue({
  el: '#vue',
  data: {
    message: 'hi #linky'
  },
  computed: {
    dynamicComponent: function() {
      return {
        template: `<div>${this.hashTags(this.message)}</div>`,
        methods: {
          someAction() {
            console.log("Action!");
          }
        }
      }
    }
  },
  methods: {
    hashTags: function(value) {
      // Replace hash tags with links
      return value.replace(/#(\S*)/g, '<a v-on:click="someAction">#$1</a>')
    }
  }
});

setTimeout(() => {
  v.message = 'another #thing';
}, 2000);
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="vue">
  <component :is="dynamicComponent" />
</div>

Vue bindings don't happen on interpolated HTML. You need something Vue sees as a template, like a partial. However, Vue only applies bindings to a partial once; you can't go back and change the template text and have it re-bind. So each time the template text changes, you have to create a new partial.

There is a <partial> tag/element you can put in your HTML, and it accepts a variable name, so the procedure is:

  • the template HTML changes
  • register new partial name for the new template HTML
  • update name variable so the new partial is rendered

It's a little bit horrible to register something new every time there's a change, so it would be preferable to use a component with a more structured template if possible, but if you really need completely dynamic HTML with bindings, it works.

The example below starts out with one message, link-ified as per your filter, and after two seconds, changes message.

You can just use message as the name of the partial for registering, but you need a computed that returns that name after doing the registering, otherwise it would try to render before the name was registered.

var v = new Vue({
  el: 'body',
  data: {
    message: 'hi #linky'
  },
  computed: {
    partialName: function() {
      Vue.partial(this.message, this.hashTags(this.message));
      return this.message;
    }
  },
  methods: {
    someAction: function() {
      console.log('Action!');
    },
    hashTags: function(value) {
      // Replace hash tags with links
      return value.replace(/#(\S*)/g, '<a v-on:click="someAction()">#$1</a>')
    }
  }
});

setTimeout(() => {
  v.$set('message', 'another #thing');
}, 2000);
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<partial :name="partialName"></partial>
like image 198
Roy J Avatar answered Sep 21 '22 14:09

Roy J