1

I use VueJs and I create the following component with it.

var ComponentTest = {
    props: ['list', 'symbole'],
    data: function(){
        return {
            regexSymbole: new RegExp(this.symbole),
        }
    },
    template: `
        <div>
            <ul>
                <li v-for="item in list" 
                    v-html="replaceSymbole(item.name)">
                </li>
            </ul>
        </div>
    `,
    methods: {
        replaceSymbole: function(name){
            return name.replace(this.regexSymbole, '<span v-on:click="test">---</span>');
        },

        test: function(event){
            console.log('Test ...');
            console.log(this.$el);
        },   
    }
};


var app = new Vue({
    el: '#app',
    components: {
        'component-test': ComponentTest,
    },
    data: {
        list: [{"id":1,"name":"@ name1"},{"id":2,"name":"@ name2"},{"id":3,"name":"@ name3"}], 
        symbole: '@'
    },
});

and this my html code

<div id="app">
    <component-test :list="list" :symbole="symbole"></component-test>
</div>

When I click on the "span" tag inside "li" tag, nothing append.

I don't have any warnings and any errors.

How I can call my component method "test" when I click in the "span" tag.

How implement click event for this case.

4
  • If you inspect the generated html, I expect that you see a literal v-on:click attribute on your span element, right? Commented Oct 22, 2018 at 19:12
  • yes I see v-on:click in all span tag Commented Oct 22, 2018 at 19:14
  • Vue does not interpret your dynamically created span tags. You could either use pure javascript or you programmatically render a Vue component into the span tag, which fires a click event. Commented Oct 22, 2018 at 19:17
  • "you cannot use v-html to compose template partials, because Vue is not a string-based templating engine. Instead, components are preferred as the fundamental unit for UI reuse and composition." (source). You also can check this answer Commented Oct 22, 2018 at 19:21

1 Answer 1

1

You cannot use vue directives in strings that you feed to v-html. They are not interpreted, and instead end up as actual attributes. You have several options:

  • Prepare your data better, so you can use normal templates. You would, for example, prepare your data as an object: { linkText: '---', position: 'before', name: 'name1' }, then render it based on position. I think this is by far the nicest solution.

    <template>
      <div>
        <ul>
          <li v-for="(item, index) in preparedList" :key="index">
            <template v-if="item.position === 'before'">
              <span v-on:click="test">{{ item.linkText }}</span>
              {{ item.name }}
            </template>
            <template v-else-if="item.position === 'after'">
              {{ item.name }}
              <span v-on:click="test">{{ item.linkText }}</span>
            </template>
          </li>
        </ul>
      </div>
    </template>
    
    <script>
    export default {
      props: ["list", "symbole"],
    
      computed: {
        preparedList() {
          return this.list.map(item => this.replaceSymbole(item.name));
        }
      },
    
      methods: {
        replaceSymbole: function(question) {
          if (question.indexOf("@") === 0) {
            return {
              linkText: "---",
              position: "before",
              name: question.replace("@", "").trim()
            };
          } else {
            return {
              linkText: "---",
              position: "after",
              name: question.replace("@", "").trim()
            };
          }
        },
    
        test: function(event) {
          console.log("Test ...");
          console.log(this.$el);
        }
      }
    };
    </script>
    
  • You can put the click handler on the surrounding li, and filter the event. The first argument to your click handler is the MouseEvent that was fired.

    <template>
      <div>
        <ul>
          <li v-for="item in list" :key="item.id" v-on:click="clickHandler"
              v-html="replaceSymbole(item.name)">
          </li>
        </ul>
      </div>
    </template>
    
    <script>
    export default {
      props: ["list", "symbole"],
    
      data() {
        return {
          regexSymbole: new RegExp(this.symbole)
        };
      },
    
      computed: {
        preparedList() {
          return this.list.map(item => this.replaceSymbole(item.name));
        }
      },
    
      methods: {
        replaceSymbole: function(name) {
          return name.replace(
            this.regexSymbole,
            '<span class="clickable-area">---</span>'
          );
        },
    
        test: function(event) {
          console.log("Test ...");
          console.log(this.$el);
        },
    
        clickHandler(event) {
          const classes = event.srcElement.className.split(" ");
    
          // Not something you do not want to trigger the event on
          if (classes.indexOf("clickable-area") === -1) {
            return;
          }
    
          // Here we can call test
          this.test(event);
        }
      }
    };
    </script>
    
  • Your last option is to manually add event handlers to your spans. I do not!!! recommend this. You must also remove these event handlers when you destroy the component or when the list changes, or you will create a memory leak.

Edit Vue Template

Sign up to request clarification or add additional context in comments.

3 Comments

thx for your answer, I can't prepare data better because my name string can contains many symboles , and yes I can't use v-html for this case so I will do in pure js.
@wpuser000 I don't understand that. The second option uses v-html and has the behaviour you expect...
I try this before <li v-for="item in list">{{ replaceSymbole(item.name) }}</li> but it render in html "<span>---</span>name" so I read the doc and I found this v-html, but I don't read well the doc because I can't use this directive for what I have tried to achieve, i just want replace all symbole with span with click listener. ex: {name:"@ big name @ with many symboles @ "} => <span onclick="test"></span> big name <span onclick="test"></span> with many symbole <span onclick="test"></span>"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.