user1898404
user1898404

Reputation: 81

Expand/collapse in Vue

I am currently making an accordion from data coming from a service (I have made an object named "groups" for example). Currently, I can click on the Group Name and it will hide/show the Description correctly, however it opens all instances. How can I make it open the group that was clicked, instead of all?

Here is my fiddle: https://jsfiddle.net/ch609uov/1/

I know I can use the open property in my groups var, however the actual data I am working with does not have that property. So, I need it to work with the isExpanded prop which I have added to my Vue instance.

var groups = {
"GROUP A": {
"name": "GROUP A",
"open": false,
"desc": "description 1",
"heading": "test",
"items": [
  "item 1"
]
},

new Vue({
el: ".vue",
data: {
groups: groups,
heading: "Plan Communications",
isExpanded: false
}
})

Upvotes: 2

Views: 19059

Answers (1)

Derek Pollard
Derek Pollard

Reputation: 7165

Modifying the resource data locally

There are a few ways you can go about doing this, the first way actually modifies the data returned from the external resource in such a way that each entry has its own open attribute:

let serviceDataExample = [{title: "some title 1"}, {title: "some title 2"}, {title: "some title 3"}];

serviceDataExample.forEach(obj => {
	obj.open = false;
});

console.log(serviceDataExample);

// assign to your data object with `this` and use the open 
// attribute individually in the template

In the above, serviceDataExample is just a mock representation of what the data might look like.

The downside to the solution above is that every time you fetch new data from the resource, you're going to have to essentially re-run the forEach before assigning it to your data object.


Without modifying the resource data locally

Doing it this way ensures that you never have to clutter the original (local) resource data with display settings:

var groups = {
  "GROUP A": {
    "name": "GROUP A",
    "open": false,
    "desc": "description 1",
    "heading": "test",
    "items": [
      "item 1"
    ]
  },
  "GROUP B": {
    "name": "GROUP B",
    "open": false,
    "desc": "description 2",
    "items": [
      "item 1",
      "item 2",
      "item 3",
      "item 4",
      "item 5",
      "item 6",
      "item 7"
    ]
  },
  "GROUP C": {
    "name": "GROUP C",
    "open": false,
    "desc": "description 3",
    "items": [
      "item 1",
      "item 2",
      "item 3",
      "item 4",
      "item 5"
    ]
  },
  "GROUP D": {
    "name": "GROUP D",
    "open": false,
    "desc": "description 4",
    "items": [
      "item 1",
      "item 2",
      "item 3",
      "item 4",
      "item 5",
      "item 6",
      "item 7"
    ]
  },
  "GROUP E": {
    "name": "GROUP E",
    "open": false,
    "desc": "description 5",
    "items": [
      "item 1",
      "item 2",
      "item 3",
      "item 4",
      "item 5"
    ]
  }
}

new Vue({
  el: ".vue",
  data: {
    groups: groups,
    heading: "Plan Communications",
    // isExpanded: false,
    expandedGroup: []
  },
  methods: {
    isExpanded(key) {
    	return this.expandedGroup.indexOf(key) !== -1;
    },
    toggleExpansion(key) {
    	if (this.isExpanded(key))
      	    this.expandedGroup.splice(this.expandedGroup.indexOf(key), 1);
        else
      	    this.expandedGroup.push(key);
    }
  }
  
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div class="container vue">
    <div v-for="(group, key) in groups"> 
    <a @click="toggleExpansion(key)" >{{group.name}}</a>
    <ul v-show="isExpanded(key)">
      <li>{{group.desc}}</li>
    </ul>
    <hr>
  </div>
</div>

Here, we are creating an external data attribute in order to keep track of the index of the group that is expanded. If the group's index exists within the local expandedGroup, then it is open, if not, it is closed.

Hope this helps!

Upvotes: 3

Related Questions