Is there a way to dynamically apply the "active" class to a Vue component when it is clicked?

Here is the structure of my Vue component:

Vue.component('list-category', {
  template: "#lc",
  props: ['data', 'category', 'search'],
  data() {
    return {
      open: false,
      categoryId: this.category
    }
  },
  mounted() {
    let isDataOpen = (d) => d.id === this.categoryId || d.children && d.children.some(isDataOpen);
  this.open = isDataOpen(this.data);
  },
  computed: {
    icon() {
      return {
        'fa-plus': !this.open,
        'fa-minus': this.open,
      }
    },
    isFolder() {
      return this.data.children && this.data.children.length
    },
    isShow() {
      return this.open ? 'show' : 'hide'
    }
  },
  methods: {
    toggle() {
      this.open = !this.open
    },
    filterByCategory(id) {
      this.categoryId = id
    }
  }
})

new Vue({
  el: '#app',
  data() {
    return {
      categories: [ /* category data here */],
      category: 7 // active category ID
    }
  }
})
.active {
  background: yellow;
}

/* CSS styles here */
.show {
  display: block !important;
}

.hide {
  display: none !important;
}
<script src="https://unpkg.com/vue"></script>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">

<div id="app">
  <div class="panel panel-default pd-search-filter">
    <div class="panel-heading">
      <h3 class="panel-title"><i class="fa fa-circle-o"></i> By Category</h3>
    </div>
    <div class="panel-body">
      <ul class="filter-category" v-for="list in categories">
        <list-category :data="list" :category="category"></list-category>
      </ul>
    </div>
  </div>
</div>

If you want to see a demo and full code, check out this link: http://jsfiddle.net/vxLhbo5m/861/

In the demo, the category "Hazard" appears to be active. However, when clicking on the category "Morata," it does not become active as expected. How can I resolve this issue?

===========================================================================

Answer №1

To update the category calculator, it is recommended to move it to a watcher (instead of using mount()) and emit/listen to events from child to parent in order to update the category and collapse the non-selected sub-tree.

You can find an updated JSFiddle demo here.

Here are the changes that need to be made:

  • Template:

    • Parent:

      • Change this:

        <div id="app">
            ...
                <list-category :data="list" :category="category"></list-category>
        
      • Add listening for the category event and update the category property in the parent component:

        <div id="app">
            ...
                <list-category :data="list" :category="category" @category="category = $event"></list-category>
        
    • Child:

      • Change this:

        <template id="lc">
            ...
                <list-category v-for="(data, index) in data.children" :key="index" :data="data" :search="search" :category="categoryId"></list-category>
        
      • Add listener for the category event and emit it to the parent component:

        <template id="lc">
            ...
                <list-category v-for="(data, index) in data.children" :key="index" :data="data" :search="search" :category="categoryId" @category="$emit('category', $event)"></list-category>
        
  • JavaScript (all within the child component):

    • Modify filterByCategory to emit the event instead of mutating the property:

      • From:

        filterByCategory(id) {
          this.categoryId = id
        }
        
      • To:

        filterByCategory(id) {
          this.$emit('category', id);
        }
        
    • Remove the mounted hook and add a watcher instead:

      • Remove mounted:

        mounted() {
          let isDataOpen = (d) => d.id === this.categoryId || d.children && d.children.some(isDataOpen);
          this.open = isDataOpen(this.data);
        },
        
      • Add a watcher to detect when the category changes in the parent component:

        watch: {
          category: {
            handler() {
              this.categoryId = this.category
              let isDataOpen = (d) => d.id === this.categoryId || d.children && d.children.some(isDataOpen);
              this.open = isDataOpen(this.data);
            },
            immediate: true
          }
        }
        

For a live demonstration, refer to the code snippet below:

Answer №2

It is not possible to directly manipulate the data of a parent element from a child component. To make changes to the parent's data, you must emit an event to the parent and then update the data accordingly.

Below is an example demonstrating how to utilize this.$emit. I had to modify the JSON data to prevent recursive calls to the same template, but this should provide you with insight on modifying the parent data element.

Vue.component('list-category', {
  template: "#lc",
  props: ['data', 'category', 'search'],
  data() {
    return {
      open: false,
      categoryId: this.category
    }
  },
  mounted() {
    let isDataOpen = (d) => d.id === this.categoryId || d.children && d.children.some(isDataOpen);
  this.open = isDataOpen(this.data);
  },
  computed: {
    icon() {
      return {
        'fa-plus': !this.open,
        'fa-minus': this.open,
      }
    },
    isFolder() {
      return this.data.children && this.data.children.length
    },
    isShow() {
      return this.open ? 'show' : 'hide'
    }
  },
  methods: {
    toggle() {
      this.open = !this.open
    },
    filterByCategory: function(id){
      this.$emit('update-active-category', id);
      console.log('Emitting: ' + id);
    }
  }
})

new Vue({
  el: '#app',
  data() {
return {
  categories: [/*Array of category objects*/],
    category: 7
    }
  },
  methods: {
    updateActiveCategory: function(id) {
    this.category = id;
  }
}
})
/* CSS Styles */
<script src="https://unpkg.com/vue"></script>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">

<div id="app">/*HTML template structure*/</div>

<template id="lc">/*List category template definition*/</template>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Prevent users from selecting elements on the mobile site

Hey there! I'm currently working on preventing users from selecting items on my mobile site. While I've been successful in doing so on a regular website using the CSS class below .unselectable { -moz-user-select: -moz-none; -khtml-user-s ...

"Exploring the Depths: How Node.js Utilizes Recursive Calls

I need assistance generating a comprehensive list of all users' flairs on a specific subreddit. To achieve this, Reddit breaks down the requests into chunks of 1,000 and offers both "before" and "after" parameters for fetching purposes. However, I am ...

Utilizing ng-disabled with a custom directive

Is it possible to achieve the following: <directiveName parameter1=value1 parameter2=value2 ng-disabled="true"> </directiveName> I tried this but couldn't get it to work and didn't find many examples of its use. However, I can togg ...

What is the process of developing a straightforward Node.js script to encapsulate a binary or executable file?

I am looking to develop a Node.js file that can seamlessly execute an executable binary file and handle all inputs and outputs, specifically targeting Windows operating systems. Reason for this: I aim to install an executable tool via npm install -g in or ...

Displaying a loading spinner image as PHP script runs

Hey there! I've been experimenting with using a script to show a loading bar while my PHP code is running. I followed the instructions on this website, but even after following the exact steps, my loading bar still isn't showing up. Any suggestio ...

Shuffle the contents of various div elements, conceal one, reveal another

I am currently working with a menu that uses the loadContent function to load pages into the #main div. This allows me to change the content of the page while keeping the menu intact. <ul> <li class="page1" onclick="loadContent('page1.php ...

displaying several gltf 3D models simultaneously on a map using mapbox gl and three js

Recently, I encountered an issue with adding glTF 3D models to a map using Mapbox GL and Three.js. It seems that while I can successfully add a single glTF model in a separate layer on the map, I am facing difficulties when trying to add multiple glTF mode ...

Angular JS function is returning before the completion of the http.get request

I'm encountering an issue where this block of code is returning before it has received all the necessary information. Here's my current implementation: function (){ .... var promise = $http.get(...) promise.then (...){ //get info nee ...

Obtain module-specific members through programmatic means

When working on a browser, the code may appear like this: //retrieve all enumerable properties of `this` function globalMems() { var g = this; var ret = {}; for (var prop in g) { ret[prop] = g[prop]; } return ret; } In Node.js, this does ...

Display a JSON encoded array using Jquery

Within an ajax call, I have a single json encoded array set: $var = json_encode($_SESSION['pictures']); The json encoded array is stored in a variable called "array" When I try to display the contents of "array" using alert, I get this respons ...

Issues with HTML marquee not functioning properly post fadeIn()

I am attempting to create a progress bar using the HTML marquee element. When the user clicks submit, I want to fadeIn the HTML marquee and fadeOut with AJAX success. However, when I click the submit button, the marquee does not fadeIn as expected. Here is ...

When executing npm run server, an error is generated saying: "sh: 1: vue-cli-service: not

I encountered an issue while setting up an existing vue project and received the following error message: admin@kali:/media/veracrypt1/themeforest-LSerfC0M-skote-vuejs-admin-dashboard-template/Admin$ npm run serve > <a href="/cdn-cgi/l/email-protec ...

Tips on restricting dates to be equal to or earlier:

I have written a code to determine if two given dates are equal or not. The code should allow for the current date to be smaller than or equal to the provided date, but it should not allow for it to be greater. var date = '10-11-2015'; var toda ...

morris.js - displaying a dynamic line chart using JSON data

These are the resources I have: clicks.json index.html The contents of my clicks.json file: [ {"day":1,"clicks":"387"}, {"day":2,"clicks":"432"}, {"day":3,"clicks":"316"}, {"day":4,"clicks":"238"}, {"day":5,"clicks":"354"}, {"da ...

React: The error message is saying that it cannot retrieve the 'handler' property because it is either undefined or null

I'm having some trouble with event handlers in my React.js project. Every time I try to create an event handler outside of the render function, I end up with an error. Can anyone help me figure out what I'm doing wrong? class CheckboxHandler ext ...

Can WebDriver (HtmlUnit, Ruby bindings) be configured to bypass JavaScript exceptions?

When attempting to load the page, HtmlUnit throws an exception and crashes my test. caps = Selenium::WebDriver::Remote::Capabilities.htmlunit(:javascript_enabled => true) driver = Selenium::WebDriver.for(:remote, :desired_capabilities => caps) drive ...

Experiencing an issue with Jest - Error: unable to access property 'forEach' of null

After watching some tutorials, I decided to create a sample project in Jest for writing tests. In a TypeScript file, I included a basic calculation function like this: Calc.cs export class Calc { public add(num1: number, num2: number): number { ...

Issue with React event hierarchy

How can I effectively manage state changes in a deep node that also need to be handled by a parent node? Let me explain my scenario: <Table> <Row prop={user1}> <Column prop={user1_col1} /> <Column prop={user1_col2} /> ...

What could be the reason for event.stopPropagation() not functioning properly with a switch statement

Could you please explain why the function event.stopPropagation() is not working on the switch element? Whenever I click on the switch, it prints the console log for the switch. However, when I click on the surrounding area (row), it logs the row event in ...

The problem with JQuery ajax arises when attempting to send a file input value

I am facing an issue when trying to send a file value to PHP using the ajax code below. The file gets uploaded successfully and stored in the database, but the problem arises when I get redirected. // form data submission $('#myForm').submit(fun ...