1

Vue is not registering event handler for HTML injected objects. How do I do this manually or what is a better way to work around my problem?

Specifically, I send a query to my server to find a token in text and return the context (surrounding text) of that token as it exists in unstructured natural language. The server also goes through the context and finds a list of those words that also happen to be in my token set.

When I render to my page I want all of these found tokens in the list to be clickable so that I can send the text of that token as a new search query. The big problem I am having is my issue does not conform to a template. The clickable text varies in number and positioning.

An example of what I am talking about is that my return may look like:

{
   "context": "When in the Course of human events, it becomes necessary for one people to dissolve the political bands which have connected",
   "chunks": ['human events', 'one people', 'political bands']
}

And the resulting output I am looking for is the sentence looks something like this in psuedocode:

When in the Course of <a @click='search("human events")'>human events</a>, it becomes necessary for <a @click='search("one people")'>one people</a> to dissolve the <a @click='search("political bands")'>political bands</a> which have connected

This is what I have tried so far though the click handler is not registered and the function never gets called:

<v-flex xs10 v-html="addlink(context.context, context.chunks)"></v-flex>

and in my methods section:

addlink: function(words, matchterms){
                for(var index in matchterms){
                    var regquery = matchterms[index].replace(this.regEscape, '\\$&');
                    var query = matchterms[index];
                    var regEx = new RegExp(regquery, "ig");
                    words = words.replace(regEx, '<a href=\'#\' v-on:click.prevent=\'doSearch("'+ query +'")\'>' + query + '</a>');
                }
                return words;
            }

As I said, this does not work and I know why. This is just showing that because of the nature of the problem is seems like regex is the correct solution but that gets me into a v-html injection situation. Is there something I can do in Vue to register the event handlers or can some one tell me a better way to load this data so I keep my links inline with the sentence and make them functional as well?

2 Answers 2

2

I've already posted one answer but I've just realised that there's a totally different approach that might work depending on your circumstances.

You could use event delegation. So rather than putting click listeners on each <a> you could put a single listener on the wrapper element. Within the listener you could then check whether the clicked element was an <a> (using event.target) and act accordingly.

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

Comments

0

Here's one way you could approach it:

<template>
    <div>
        <template v-for="segment in textSegments">
            <a v-if="segment.link" href="#" @click.prevent="search(segment.text)">
                {{ segment.text }}
            </a>
            <template v-else>
                {{ segment.text }}
            </template>
        </template>
    </div>
</template>

<script>
export default {
    data () {
        return {
            "context": "When in the Course of human events, it becomes necessary for one people to dissolve the political bands which have connected",
            "chunks": ['human events', 'one people', 'political bands']
        }
    },

    computed: {
        textSegments () {
            const chunks = this.chunks

            // This needs escaping correctly
            const re = new RegExp('(' + chunks.join('|') + ')', 'gi')

            // The filter removes empty strings
            const segments = this.context.split(re).filter(text => text)

            return segments.map(segment => {
                return {
                    link: segment.match(re),
                    text: segment
                }
            })
        }
    },

    methods: {
        search (chunk) {
            console.log(chunk)
        }
    }
}
</script>

I've parsed the context text into an array of segments that can then be handled cleanly using Vue's template syntax.

I've used a single RegExp and split, which will not discard matches if you wrap them in a capture group, (...).

Going back to your original example, v-html only supports native HTML, not Vue template syntax. So you can add events using onclick attributes but not @click or v-on:click. However, using onclick wouldn't provide easy access to your search method, which is scoped to your component.

1 Comment

I really like the way this was addressed. I went about it a little differently in my implementation because of my code structure but this was definitely the inspiration for the solution I eventually put into place. I decided to do a bit more data processing when I receive the data and kept the v-if/v-else in the page html (I am not setting up a true Vue app). Thanks for the solution!

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.