Vue.js中的动态html元素

时间:2016-09-15 17:09:52

标签: vue.js

如何动态地向内容添加元素?示例如下:

<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>

问题是,如果我按下链接,则不会触发任何操作。 Vue看不到新的元素。

6 个答案:

答案 0 :(得分:9)

我刚刚了解了SELECT a.family, coalesce(sum(b.tpt), 0) AS total FROM tbl AS a LEFT JOIN tbl AS b ON a.tool = b.tool AND a.family != b.family GROUP BY a.family ,它似乎非常适合您的需求。使用$compile的非常简单的指令可以避免所有注册。

$compile
Vue.directive('dynamic', function(newValue) {
    this.el.innerHTML = newValue;
    this.vm.$compile(this.el);
});

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

setTimeout(() => {
  v.$set('message', 'another #thing');
}, 2000);

答案 1 :(得分:6)

<强>更新 基于this answer,您可以在Vue 2中执行类似的动态模板组件。您实际上可以在computed部分设置组件规范并使用:is

绑定它

&#13;
&#13;
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);
&#13;
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="vue">
  <component :is="dynamicComponent" />
</div>
&#13;
&#13;
&#13;

Vue绑定不会发生在插值HTML上。您需要Vue视为模板的内容,例如a partial。但是,Vue仅将绑定应用于部分一次;您无法返回并更改模板文本并重新绑定。因此,每次模板文本更改时,您都必须创建一个新的部分。

您可以在HTML中添加<partial>标记/元素,并且它接受变量名称,因此过程为:

  • 模板HTML更改
  • 为新模板HTML
  • 注册新的部分名称
  • 更新名称变量,以便呈现新的部分

每次发生变化时注册新内容都有点可怕,所以如果可能的话,最好使用具有更结构化模板的组件,但如果你真的需要完全动态的HTML绑定,它的工作原理。

以下示例以一条消息开头,根据您的过滤器进行链接,并在两秒后更改message

您可以使用message作为注册部分的名称,但是您需要一个在执行注册后返回该名称的计算器,否则它将尝试在注册名称之前呈现。

&#13;
&#13;
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);
&#13;
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<partial :name="partialName"></partial>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

partial已从VueJS 2(https://vuejs.org/v2/guide/migration.html#Vue-partial-removed

中删除

更好的方法可能是创建一个处理其内容并创建适当DOM元素的组件

上述组件将通过可点击链接替换主题标签

<process-text>Hi #hashtag !</process-text>
Vue.component('process-text', {
    render: function (createElement) {
        var hashtagRegex = /(^|\W)(#[a-z\d][\w-]*)/ig
        var text = this.$slots.default[0].text
        var list = text.split(hashtagRegex)
        var children = []
        for (var i = 0; i < list.length; i++) {
            var element = list[i]
            if (element.match(hashtagRegex)) {
                children.push(createElement('a', {
                attrs: {
                    href: 'https://www.google.fr/search?q=' + element,
                    target: "_blank"
                    },
                domProps: {
                    innerHTML: element
                    }
                }))
            } else {
                children.push(element)
            }
        }
    }
    return createElement('p', {}, children)  // VueJS expects root element
})

答案 3 :(得分:0)

我发现使用自定义html工作正常的最佳解决方案看起来像这样,就像你每次html属性更改时创建新组件一样。实际上没有人这样做,我们只使用computed属性来创建新组件。

它的外观如下:

new Vue({
  el: "#root",
  data: {
      value: '',
      name: 'root',
      htmlData: '<div><input @input="onInputProxy($event)" ' +
                            'v-model="value" ' + 
                            'v-for="i in 3" ' + 
                            ':ref="`customInput${i}`"></div>'
  },
  computed: {
    // our component is computed property which returns the dict
    htmlDataComponent () {
      return {
        template: this.htmlData, // we use htmlData as template text

        data() {
          return {
            name: 'component',
            value: ''
          }
        },
        created () {
          // value of "this" is formComponent
          console.log(this.name + ' created');
        },
        methods: {
          // proxy components method to parent method,
          // actually you done have to
          onInputProxy: this.onInput
        }
      }
    }
  },
  methods: {
    onInput ($event) {
      // while $event is proxied from dynamic formComponent
      // value of "this" is parent component
      console.log(this.name + ' onInput');

      // use refs to refer to real components value
      console.log(this.$refs.htmlDataComponent.value);
      console.log(this.$refs.htmlDataComponent.$refs.customInput1);
      console.log(this.$refs.htmlDataComponent.$refs.customInput2);
      console.log(this.$refs.htmlDataComponent.$refs.customInput3);
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js">
</script>

<div id="root">
  <component ref="htmlDataComponent" 
             v-if="htmlData"
             :is="htmlDataComponent"></component>
</div>

我没有检查它的内存效率,但看起来效果很好。

答案 4 :(得分:0)

@RoyJ答案的修改版本,可在Vue.js v2.6.10中使用

new Vue({
    ...,
    computed: {
        inner_html() {
            return ...; // any raw html
        },
    },
    directives: {
        dynamic: {
            bind(el, binding) {
                el.innerHTML = binding.value;
            },
            update(el, binding) {
                el.innerHTML = binding.value;
            },
        },
    },
    template: `<div v-dynamic='inner_html'></div>`,
});

答案 5 :(得分:0)

在Vue.js 2中更容易:

new Vue({
    ...,
    computed: {
        inner_html() {
            return ...; // any raw html
        },
    },
    template: `<div v-html='inner_html'></div>`,
});