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

时间:2018-03-25 06:44:14

标签: javascript css vue.js vuejs2 vue-component

我的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>
&#13;
&#13;
&#13;

似乎您需要查看演示和完整代码

它是这样的:http://jsfiddle.net/vxLhbo5m/861/

从演示中看到的类别危险活跃。如果我点击morata类别,它就不活跃了。虽然我已经制作了代码

我该如何解决这个问题?

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

2 个答案:

答案 0 :(得分:1)

你必须将类别计算器移动到观察者(而不是mount())并发出/收听从孩子到父母的一些事件来更新类别并且崩溃非选定的子树。

<强> Updated JSFiddle here

的变化:

  • 模板:

    • 父:

      • 自:

        <div id="app">
            ...
                <list-category :data="list" :category="category"></list-category>
        
      • 添加监听category事件并更新父级的category属性:

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

      • 自:

        <template id="lc">
            ...
                <list-category v-for="(data, index) in data.children" :key="index" :data="data" :search="search" :category="categoryId"></list-category>
        
      • 收听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(所有孩子):

    • filterByCategory更改为发出事件而不是改变属性:

      • 自:

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

        filterByCategory(id) {
          this.$emit('category', id);
        }
        
    • 删除mounted挂钩并添加观察者:

      • 删除已安装:

        mounted() {
          let isDataOpen = (d) => d.id === this.categoryId || d.children && d.children.some(isDataOpen);
          this.open = isDataOpen(this.data);
        },
        
      • 添加观察者,以便在父级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
          }
        }
        

演示:

&#13;
&#13;
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
    }
  }
})
&#13;
.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;
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

您无法控制子组件中父元素的数据。为了更改父母的数据,您需要将更改发送到父级,然后更改父级的数据。

请查看以下内容,了解如何使用this.$emit。我知道我必须更改json数据以避免对同一模板的递归调用,但现在您已了解如何更改父数据元素。

&#13;
&#13;
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;
  }
}
})
&#13;
.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;
}
&#13;
<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>
&#13;
&#13;
&#13;