2016-09-15 7 views
3

Как можно динамически добавлять элементы в контент? Пример ниже:Динамические элементы html в 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 не видит новых элементов.

ответ

3

Связывание Vue не происходит при интерполированном HTML. Вам нужно что-то Vue видит как шаблон, например a partial. Однако Vue применяет привязки только к частичной однократной; вы не можете вернуться и изменить текст шаблона и повторно привязать его. Поэтому каждый раз, когда текст шаблона изменяется, вы должны создать новое частичное.

Существует <partial> тег/элемент, который можно положить в HTML, и он принимает имя переменной, поэтому процедура:

  • шаблон HTML изменения
  • регистра новое частичное имя для нового шаблон HTML
  • обновление имя переменной, чтобы новый частичный визуализируется

Это немного страшно зарегистрировать что-то новое каждый раз, когда есть изменения, так это было бы предпочтительнее использовать компонент с более структурированным шаблоном, если это возможно, но если вам действительно нужен полностью динамический HTML с привязками, он работает.

Приведенный ниже пример начинается с одного сообщения, связанного с вашим фильтром, и через две секунды изменяется message.

Вы можете просто использовать message как имя частичного для регистрации, но вам нужно вычисленное значение, которое возвращает это имя после регистрации, иначе оно попытается отобразить до того, как имя будет зарегистрировано.

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>

+0

Я хочу сказать вам большое спасибо, я искал об этом 3 днях .., спросил в гроте, и все сказали, что это невозможно, но вы сделали это. Но вы сказали, что это не очень хорошее решение, моя цель - заменить '# hashtags',' @ упоминает' ссылками, может быть, есть лучшее решение? Конечно, я могу подготовить его на стороне сервера, но он выглядит лучше, когда вся логика подана с одной стороны. – user2058653

4

Я только что узнал о $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);
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script> 
 
<div v-dynamic="messageAsHtml"></div>

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 
}) 
0

Лучшее решение, которое я нашел, который отлично работает с помощью пользовательского HTML это выглядит так, как это вы вроде создать новый компонент каждый раз меняется свойство HTML. На самом деле никто этого не делал, мы просто используем вычисляемое свойство для создания нового компонента.

Вот как это выглядит:

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>

Я не проверял его эффективности памяти, но это выглядит, как работает просто отлично.

Смежные вопросы