Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
After some research the following suggestion by Mr. Evan You was found:
https://github.com/vuejs/vue/issues/7349#issuecomment-354937350
So without any hesitation I gave it a try:
Component template
<template>
<div v-on='{ click: dataType === `section` ? toggleSectionElements : null }'>
... magic
<template>
JS Logic
<script>
export default {
name: `product-section`,
props: [`section`, `sectionName`, `depth`],
methods: {
toggleSectionElements() {
... magic
computed: {
dataType() {
if (this.$props.section.sections || this.$props.depth === 0) {
return `section`
} else {
return `element`
</script>
But for described case it results in error during rendering:
[Vue warn]: Invalid handler for event "click": got null
Can someone please suggest what has been done wrong? :thinking:
Update
The way Data Model
looks like:
DataModel: {
mainSectionA: {
sections: {
sectionA: {
sections: {
elementA: { values: { ... } },
elementB: { values: { ... } }
values: { ... }
sectionB: {
elementA: { values: { ... } },
elementB: { values: { ... } }
values: { ... }
mainSectionB: {
sections: {
elementA: { values: { ... } },
elementB: { values: { ... } },
elementC: { values: { ... } },
... elements
values: { ... }
Just change it to the below and it will work
v-on="condition ? { mouseover: handler } : {}"
or, if your handler is called mouseover
v-on="condition ? { mouseover } : {}"
Instead of polluting your template with ternary logic, you should actually perform the check inside the click handler instead. It not only makes your template more readable, but also makes maintaining the code easier since all logic has been abstracted and delegated to the event handler's callback instead.
Quick solution
Therefore the quick solution is to actually ensure that the toggleSectionElements()
will only work when a correct dataType
is present. This can be achieved by using a guard clause:
toggleSectionElements() {
// Guard clause to prevent further code execution
if (this.dataType() !== 'section')
return;
// Magic here
Even better, is that if separate handlers should be assigned to each dataType
: you can then create a factory function for that purpose:
methods: {
// This is just a factory function
toggleElements() {
switch (this.dataType()) {
case 'section':
return this.toggleSectionElements;
case 'element':
// Something else...
toggleSectionElements() {
// Magic for section element
Suggestion: using atomic components
Since it might be costly to bind click event handlers to elements that end up doing nothing, you can also break down your component to be more atomic. The collection element will be responsible of receiving an array of "section" or "element", and each "section"/"element" will have its own component, something like this:
You have a collection component, say <my-collection>
, that holds all "section" and "element" components
"section" component will use the <my-section>
component
"element" component will use the <my-element>
component
This is when VueJS becomes really powerful: you can use dynamic component inside <my-collection>
to determine which component to use depending on the dataType
encountered.
This is done by running a v-for
through the collection, and then using v-bind:is="..."
to determine whether a specific collection item should be using "section" or "element". I understand that this is probably going to go out of scope of your original question, but it's a worthwhile design to consider:
const collectionComponent = Vue.component('my-collection', {
template: '#my-collection-component',
data: function() {
return {
collection: [{
dataType: 'section',
description: 'Hello I am section 1'
dataType: 'element',
description: 'Hello I am element 1'
dataType: 'section',
description: 'Hello I am section 2'
dataType: 'element',
description: 'Hello I am element 2'
methods: {
componentToUse(dataType) {
return 'my-' + dataType;
const sectionComponent = Vue.component('my-section', {
template: '#my-section-component',
props: ['itemData'],
methods: {
toggle() {
console.log('Doing some magic.');
const elementComponent = Vue.component('my-element', {
template: '#my-element-component',
props: ['itemData']
new Vue({
el: '#app'
.box {
border: 1px solid #999;
cursor: pointer;
margin: 10px;
padding: 10px;
.box:hover {
background-color: #eee;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<my-collection />
<script type="text/x-template" id="my-collection-component">
<component
v-for="(item, i) in collection"
v-bind:key="i"
v-bind:is="componentToUse(item.dataType)"
v-bind:itemData="item" />
</script>
<script type="text/x-template" id="my-section-component">
<div @click="toggle" class="box">
<h1>{{ itemData.dataType }}</h1>
<p>{{ itemData.description }}</p>
<p>Clicking on me will invoke a section-specific logic</p>
</script>
<script type="text/x-template" id="my-element-component">
<div class="box">
<h1>{{ itemData.dataType }}</h1>
<p>{{ itemData.description }}</p>
<p>Clicking on me will do nothing</p>
</script>
–
–
–
in the not-equal case you pass null, but the value on click expects a function. you can try an emptry function:
click: dataType === `section` ? toggleSectionElements : ()=>{}
In Vue 3 you can pass null
to the listener. Combining it with optional chaining you can do this:
@click="handler?.() || null"
Same for old browsers:
@click="handler ? handler() : null"
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.