自定义手风琴在 Vue 中一次切换一个项目
Custom Accordion Toggle one Item at a Time in Vue
我将 Vue 与 Nuxt 2 一起使用,但我的自定义手风琴组件出现问题。
我想一次切换一个手风琴。但实际发生的情况是,如果我单击第一个手风琴 Link,第一个 Link(accordion) 的正文将打开。在不关闭第一个的情况下,如果我在第二个link(手风琴)上打开,第二个手风琴体也会打开。我想避免。如果我想打开其他手风琴,打开的应该先关闭。那么我如何在不使用任何库的情况下以 vue 方式实现这一点。
手风琴切换过程:
我正在使用 ACTIVE 值通过在我的锚标签上添加 aria-expended = "true" 来切换手风琴。同时,我根据 ACTIVE 值显示或隐藏 accordion-item-body。
我的组件手风琴:
<template>
<div id="accordion" ref="accordion" class="accordion">
<slot />
</div>
</template>
<script lang="ts">
import Vue from "vue"
import { props } from "./props"
export default Vue.extend({
name: "Accordion",
props,
created() {
this.$nuxt.$off("eventName")
this.$nuxt.$on("eventName", ($event: any) => this.toggleActive($event))
},
methods: {
toggleActive(e: any) {
console.log(e)
},
},
})
</script>
我的 AccordionItem 组件:
<template>
<div class="accordion-item" role="tab">
<div class="accordion-item-header">
<a
:aria-expanded="active"
@click.prevent=";(active = !active), $nuxt.$emit('eventName', active)"
>
{{ title }}
<span
class="material-icons"
>
expand_more
</span>
</a>
</div>
<transition name="accordion">
<div v-show="active" class="accordion-item-body" role="tabpanel">
<slot />
</div>
</transition>
</div>
</template>
<script lang="ts">
import Vue from "vue"
export default Vue.extend({
name: "AccordionItem",
props: {
title: {
type: String,
required: true,
},
id: {
type: String,
required: true,
},
},
data() {
return {
active: false,
}
},
})
</script>
父项中 Accordion 和 AccordionItem 的使用:
<Accordion :items="items">
<AccordionItem
v-for="item in items"
:id="item.id"
:key="item.id"
:title="item.title"
>
<div v-html="item.description"></div>
</AccordionItem>
</Accordion>
这是一个简单但有效的解决方案(我当然没有实现样式,但功能运行良好)。
对于包装器
<template>
<div>
<accordion
v-for="(item, index) in list"
:key="item.id"
:item="item"
:active-index="currentlyActiveIndex"
:item-index="index"
@update:itemIndex="currentlyActiveIndex = $event"
></accordion>
</div>
</template>
<script>
export default {
name: 'AccordionList',
data() {
return {
list: [
{ id: 1, name: 'hello' },
{ id: 2, name: 'world' },
{ id: 3, name: 'swag' },
],
currentlyActiveIndex: null,
}
},
}
</script>
Accordion.vue
<template>
<div>
<button @click="updateIndex">get this index: {{ itemIndex }}</button>
<span v-show="activeIndex === itemIndex">{{ item.name }}</span>
</div>
</template>
<script>
export default {
name: 'Accordion',
props: {
item: {
type: Object,
default: () => {},
},
activeIndex: {
type: Number,
default: null,
},
itemIndex: {
type: Number,
default: null,
},
},
methods: {
updateIndex() {
console.log('index updated:', this.itemIndex)
this.$emit('update:itemIndex', this.itemIndex)
},
},
}
</script>
这也可以通过类似 { id: 1, name: 'hello', isActive: false }
的方法来实现,更简洁一些,但这两种方法在这里都有效。
PS:我也不会用TS,但是我相信你可以自己想出这部分(我猜不是这里的拦截器)。
我将 Vue 与 Nuxt 2 一起使用,但我的自定义手风琴组件出现问题。
我想一次切换一个手风琴。但实际发生的情况是,如果我单击第一个手风琴 Link,第一个 Link(accordion) 的正文将打开。在不关闭第一个的情况下,如果我在第二个link(手风琴)上打开,第二个手风琴体也会打开。我想避免。如果我想打开其他手风琴,打开的应该先关闭。那么我如何在不使用任何库的情况下以 vue 方式实现这一点。
手风琴切换过程: 我正在使用 ACTIVE 值通过在我的锚标签上添加 aria-expended = "true" 来切换手风琴。同时,我根据 ACTIVE 值显示或隐藏 accordion-item-body。
我的组件手风琴:
<template>
<div id="accordion" ref="accordion" class="accordion">
<slot />
</div>
</template>
<script lang="ts">
import Vue from "vue"
import { props } from "./props"
export default Vue.extend({
name: "Accordion",
props,
created() {
this.$nuxt.$off("eventName")
this.$nuxt.$on("eventName", ($event: any) => this.toggleActive($event))
},
methods: {
toggleActive(e: any) {
console.log(e)
},
},
})
</script>
我的 AccordionItem 组件:
<template>
<div class="accordion-item" role="tab">
<div class="accordion-item-header">
<a
:aria-expanded="active"
@click.prevent=";(active = !active), $nuxt.$emit('eventName', active)"
>
{{ title }}
<span
class="material-icons"
>
expand_more
</span>
</a>
</div>
<transition name="accordion">
<div v-show="active" class="accordion-item-body" role="tabpanel">
<slot />
</div>
</transition>
</div>
</template>
<script lang="ts">
import Vue from "vue"
export default Vue.extend({
name: "AccordionItem",
props: {
title: {
type: String,
required: true,
},
id: {
type: String,
required: true,
},
},
data() {
return {
active: false,
}
},
})
</script>
父项中 Accordion 和 AccordionItem 的使用:
<Accordion :items="items">
<AccordionItem
v-for="item in items"
:id="item.id"
:key="item.id"
:title="item.title"
>
<div v-html="item.description"></div>
</AccordionItem>
</Accordion>
这是一个简单但有效的解决方案(我当然没有实现样式,但功能运行良好)。
对于包装器
<template>
<div>
<accordion
v-for="(item, index) in list"
:key="item.id"
:item="item"
:active-index="currentlyActiveIndex"
:item-index="index"
@update:itemIndex="currentlyActiveIndex = $event"
></accordion>
</div>
</template>
<script>
export default {
name: 'AccordionList',
data() {
return {
list: [
{ id: 1, name: 'hello' },
{ id: 2, name: 'world' },
{ id: 3, name: 'swag' },
],
currentlyActiveIndex: null,
}
},
}
</script>
Accordion.vue
<template>
<div>
<button @click="updateIndex">get this index: {{ itemIndex }}</button>
<span v-show="activeIndex === itemIndex">{{ item.name }}</span>
</div>
</template>
<script>
export default {
name: 'Accordion',
props: {
item: {
type: Object,
default: () => {},
},
activeIndex: {
type: Number,
default: null,
},
itemIndex: {
type: Number,
default: null,
},
},
methods: {
updateIndex() {
console.log('index updated:', this.itemIndex)
this.$emit('update:itemIndex', this.itemIndex)
},
},
}
</script>
这也可以通过类似 { id: 1, name: 'hello', isActive: false }
的方法来实现,更简洁一些,但这两种方法在这里都有效。
PS:我也不会用TS,但是我相信你可以自己想出这部分(我猜不是这里的拦截器)。