简体   繁体   English

如果元素单击vue组件,如何添加活动类?

[英]How can I add class active if the element click on the vue component?

My vue component like this : 我的Vue组件是这样的:

 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: [{ id: 1, name: 'England', children: [{ id: 3, name: 'Chelsea', children: [{ id: 7, name: 'Hazard' }, { id: 8, name: 'Morata' } ] }, { id: 4, name: 'Manchester United', children: [{ id: 9, name: 'Pogba' }, { id: 10, name: 'Lukaku' } ] } ] }, { id: 2, name: 'Spain', children: [{ id: 5, name: 'Real Madrid', children: [{ id: 11, name: 'Ronaldo' }, { id: 12, name: 'Bale' } ] }, { id: 6, name: 'Barcelona', children: [{ id: 13, name: 'Messi' }, { id: 14, name: 'Suarez' } ] }, ] } ], category: 7 } } }) 
 .active { background: yellow; } .pd-search-filter > .panel-body ul.filter-category { padding-left: 0; list-style: none; margin: 0 -15px 0; } .pd-search-filter > .panel-body ul.filter-category > li a { display: block; padding: 10px 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .pd-search-filter > .panel-body ul.filter-category > li a:last-child { padding-left: 45px; } .pd-search-filter > .panel-body ul.filter-category > li a:focus, .pd-search-filter > .panel-body ul.filter-category > li a:hover { background-color: #eeeeee; text-decoration: none; } .pd-search-filter > .panel-body ul.filter-category > li a + ul { padding-left: 0; list-style: none; } .pd-search-filter > .panel-body ul.filter-category > li a + ul > li > a { padding-left: 30px; } .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> <template id="lc"> <li> <!--parent--> <a v-if="isFolder" href="javascript:" @click="toggle"> <span class="fa fa-fw" :class="icon"></span> {{data.name}} </a> <!--if not folding, we do not need an binding event--> <a v-else href="javascript:" :title="data.name" :class="{active: data.id === categoryId}" @click="filterByCategory(data.id)"><span class="fa fa-fw fa-circle-o"></span> {{data.name}}</a> <!--children--> <ul v-if="isFolder" :class="isShow"> <list-category v-for="(data, index) in data.children" :key="index" :data="data" :search="search" :category="categoryId"></list-category> </ul> </li> </template> 

Seems you need to see demo and full code 似乎您需要查看演示和完整代码

It's like this : http://jsfiddle.net/vxLhbo5m/861/ 就像这样: http : //jsfiddle.net/vxLhbo5m/861/

From demo seen category hazard active. 从演示中可以看到类别危险活动。 If I click on morata category, it is not active. 如果我单击morata类别,则该类别无效。 Whereas I have made the code 我已经编写了代码

How can I solve this problem? 我怎么解决这个问题?

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

You would have to move the category calculator to a watcher (instead of mount() ) and emit/listen to some events from child to parent to update the category and collapse the non-selected sub-tree. 您将必须将类别计算器移至观察程序(而不是mount() ),并发出/监听从子级到父级的某些事件,以更新类别并折叠未选择的子树。

Updated JSFiddle here . 在此更新了JSFiddle

Changes: 变化:

  • Template: 模板:

    • Parent: 家长:

      • From: 从:

         <div id="app"> ... <list-category :data="list" :category="category"></list-category> 
      • Adding listening to the category event and updating the category property at parent: 添加侦听category事件并更新父级的category属性:

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

      • From: 从:

         <template id="lc"> ... <list-category v-for="(data, index) in data.children" :key="index" :data="data" :search="search" :category="categoryId"></list-category> 
      • Listen to the category event and emit it up to the parent: category事件并将其发出给父对象:

         <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 in child): JavaScript(全部包含在子代码中):

    • Change filterByCategory to emit event instead of mutating property: filterByCategory更改为发出事件,而不是更改属性:

      • From: 从:

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

         filterByCategory(id) { this.$emit('category', id); } 
    • Remove mounted hook and add watcher: 卸下已mounted挂钩并添加观察者:

      • Remove mounted: 卸下已安装:

         mounted() { let isDataOpen = (d) => d.id === this.categoryId || d.children && d.children.some(isDataOpen); this.open = isDataOpen(this.data); }, 
      • Add watcher to pick up when category changes in the parent: 添加观察者以在父category发生更改时接听:

         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 } } 

Demo: 演示:

 Vue.component('list-category', { template: "#lc", props: ['data', 'category', 'search'], data() { return { open: false, categoryId: this.category } }, 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.$emit('category', id); } }, 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 } } }) new Vue({ el: '#app', data() { return { categories: [{ id: 1, name: 'England', children: [{ id: 3, name: 'Chelsea', children: [{ id: 7, name: 'Hazard' }, { id: 8, name: 'Morata' } ] }, { id: 4, name: 'Manchester United', children: [{ id: 9, name: 'Pogba' }, { id: 10, name: 'Lukaku' } ] } ] }, { id: 2, name: 'Spain', children: [{ id: 5, name: 'Real Madrid', children: [{ id: 11, name: 'Ronaldo' }, { id: 12, name: 'Bale' } ] }, { id: 6, name: 'Barcelona', children: [{ id: 13, name: 'Messi' }, { id: 14, name: 'Suarez' } ] }, ] } ], category: 7 } } }) 
 .active { background: yellow; } .pd-search-filter > .panel-body ul.filter-category { padding-left: 0; list-style: none; margin: 0 -15px 0; } .pd-search-filter > .panel-body ul.filter-category > li a { display: block; padding: 10px 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .pd-search-filter > .panel-body ul.filter-category > li a:last-child { padding-left: 45px; } .pd-search-filter > .panel-body ul.filter-category > li a:focus, .pd-search-filter > .panel-body ul.filter-category > li a:hover { background-color: #eeeeee; text-decoration: none; } .pd-search-filter > .panel-body ul.filter-category > li a + ul { padding-left: 0; list-style: none; } .pd-search-filter > .panel-body ul.filter-category > li a + ul > li > a { padding-left: 30px; } .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" @category="category = $event"></list-category> </ul> </div> </div> </div> <template id="lc"> <li> <!--parent--> <a v-if="isFolder" href="javascript:" @click="toggle"> <span class="fa fa-fw" :class="icon"></span> {{data.name}} </a> <!--if not folding, we do not need an binding event--> <a v-else href="javascript:" :title="data.name" :class="{active: data.id === categoryId}" @click="filterByCategory(data.id)"><span class="fa fa-fw fa-circle-o"></span> {{data.name}}</a> <!--children--> <ul v-if="isFolder" :class="isShow"> <list-category v-for="(data, index) in data.children" :key="index" :data="data" :search="search" :category="categoryId" @category="$emit('category', $event)"></list-category> </ul> </li> </template> 

You cannot control the data of a parent element from the child component. 您无法通过子组件控制父元素的数据。 In order to change the parent's data yyou would need to emit the change to the parent and than change the data from the parent. 为了更改父级的数据,您需要将更改发送给父级,而不是更改父级的数据。

Please find the below to have an idea how to use the this.$emit . 请找到以下内容以了解如何使用this.$emit I know I had to change the json data to avoid recursive calls to the same template, but now you have an idea on how to change the parent data element. 我知道我必须更改json数据以避免对同一模板进行递归调用,但是现在您对如何更改父数据元素有了一个了解。

 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: [{ id: 1, name: 'England', children: [{ id: 3, name: 'Chelsea', children: [{ id: 7, name: 'Hazard' }, { id: 8, name: 'Morata' } ] }, { id: 4, name: 'Manchester United', children: [{ id: 9, name: 'Pogba' }, { id: 10, name: 'Lukaku' } ] } ] }, { id: 2, name: 'Spain', children: [{ id: 5, name: 'Real Madrid', children: [{ id: 11, name: 'Ronaldo' }, { id: 12, name: 'Bale' } ] }, { id: 6, name: 'Barcelona', children: [{ id: 13, name: 'Messi' }, { id: 14, name: 'Suarez' } ] }, ] } ], category: 7 } }, methods: { updateActiveCategory: function(id) { this.category = id; } } }) 
 .active { background: yellow !important; } .pd-search-filter > .panel-body ul.filter-category { padding-left: 0; list-style: none; margin: 0 -15px 0; } .pd-search-filter > .panel-body ul.filter-category > li a { display: block; padding: 10px 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .pd-search-filter > .panel-body ul.filter-category > li a:last-child { padding-left: 45px; } .pd-search-filter > .panel-body ul.filter-category > li a:focus, .pd-search-filter > .panel-body ul.filter-category > li a:hover { background-color: #eeeeee; text-decoration: none; } .pd-search-filter > .panel-body ul.filter-category > li a + ul { padding-left: 0; list-style: none; } .pd-search-filter > .panel-body ul.filter-category > li a + ul > li > a { padding-left: 30px; } .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" @update-active-category="updateActiveCategory"> </list-category> </ul> </div> </div> </div> <template id="lc"> <li> <!--parent--> <a v-if="isFolder" href="javascript:" @click="toggle"> <span class="fa fa-fw" :class="icon"></span> {{data.name}} </a> <!--if not folding, we do not need an binding event--> <a v-else href="javascript:" :title="data.name" :class="{active: data.id === category}" @click="filterByCategory(data.id)" @update-active-category="filterByCategory"><span class="fa fa-fw fa-circle-o"></span> {{data.name}}</a> <!--children--> <ul v-if="isFolder" :class="isShow"> <list-category v-for="(data, index) in data.children" :key="index" :data="data" :search="search" :category="category" @update-active-category="filterByCategory"></list-category> </ul> </li> </template> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM