Vue.js:在页面中多次包含相同的组件实例

标签 vue.js vuejs2 vue-component

我想要完成的事情: 我有一些过滤器显示在页面上以过滤页面上显示的产品。在移动设备中,我想将这些过滤器隐藏在一个按钮后面,一旦按下该按钮,就会在侧面的滑出菜单中显示过滤器。

虽然我可以在页面上复制两次相同的组件,但这些组件并不是完全相同的实例,也就是说,点击过滤器会触发该功能来过滤页面上的产品,但它会设置自己的数据属性,我用它来表示“如果数据属性‘selected’为真,则向组件添加一个‘selected’类。当我调整窗口大小时,组件的另一个实例没有标记为‘selected’数据属性'真实'。

我希望如此,因为从文档中:

Notice that when clicking on the buttons, each one maintains its own, separate count. That’s because each time you use a component, a new instance of it is created.

...但是最好的方法是什么?

我尝试过在组件上设置一个类“mobile”的想法,.mobile css 会以不同的方式设置组件的样式,但我需要它在嵌套的地方断开。

例如

<body>
   <header>
      <!-- desktop -->
      <guitar-filters>
   <header>

   <!-- mobile -->
   <guitar-filters>
</body

这是我的 Vue 组件“guitar-filters”,它显示了几个名为“instrument-filter”的组件:

Vue.component('guitar-filters', {

    data: function() {

        return {
            isMobile: false
        }

    },

    mounted: function() {

        var comp = this;
        this.setIsMobile();

        window.addEventListener('resize', function() {
            comp.setIsMobile();
        });

    },

    methods: {

        setIsMobile: function() {

            this.isMobile = (window.innerWidth <= 900) ? true : false;

        }

    },

    template: `
        <ul class="filters" :class="{mobile: isMobile}">

        <li>
            All
        </il>

        <li>
            Series
            <ul>
                <instrument-filter filter-by="series" filter="All">All</instrument-filter>
                <instrument-filter filter-by="series" filter="Frontier">Frontier</instrument-filter>
                <instrument-filter filter-by="series" filter="Legacy">Legacy</instrument-filter>
                <instrument-filter filter-by="series" filter="USA">USA</instrument-filter>
            </ul>
        </li>

        <li>
            Body Shape
            <ul>
                <instrument-filter filter-by="bodyType" filter="All">All</instrument-filter>
                <instrument-filter filter-by="bodyType" filter="Concert">Concert</instrument-filter>
                <instrument-filter filter-by="bodyType" filter="Concertina">Concertina</instrument-filter>
                <instrument-filter filter-by="bodyType" filter="Concerto">Concerto</instrument-filter>
                <instrument-filter filter-by="bodyType" filter="Orchestra">Orchestra</instrument-filter>
            </ul>
        </li>

    </ul>
    `

});

仪器过滤器组件:

Vue.component('instrument-filter', {

    data: function() {

        return {
            selected: false
        }

    },

    props : [
        'filterBy',
        'filter'

    ],

    methods: {

        addFilter: function() {
            this.$root.$emit('addFilter',{filterBy: this.filterBy,filter: this.filter});
        },

        clearFilter: function() {
            this.$root.$emit('clearFilter',{filterBy: this.filterBy,filter: this.filter});
        }

    },

    template: `
        <li :class="{ 'selected' : selected }" @click="selected = !selected; selected ? addFilter() : clearFilter()"><slot></slot></li>
    `

});

.css:

ul.filters > li > ul > li.selected::before {
   content: "✔️";
   ...
}

目标是让过滤器在两个实例中都具有“选定”类。如果我单击“音乐会”主体形状,然后将窗口大小调整为移动断点,则该过滤器组件的其他实例也将被选中。

编辑:我可以破解这个。我可以使用 javascript 移动该组件的一个实例,但我正在学习 Vue,并且想以 Vue 的方式和最佳实践来做到这一点。

最佳答案

有许多不同的方法可以处理这个问题。看起来您已经开始沿着事件总线路径前进。另一种选择是使用共享应用程序状态(请参阅 Vuex )。

我所做的与共享状态类似,但只是使用应用程序(同样适用于公共(public)父组件)数据。共享对象被传递给组件的两个实例。如果选择了一个项目,则会切换相应的条目。由于对象是共享的,因此两个组件保持同步。

如果没有共同的父组件,您将不得不查看事件或状态。

看看是否有帮助。

Vue.component('guitar-filters', {

    props: [ 'data' ],

    data: function() {
        return {
            isMobile: false
        }
    },

    mounted: function() {
        var comp = this;
        this.setIsMobile();
        window.addEventListener('resize', function() {
            comp.setIsMobile();
        });
    },

    methods: {
        setIsMobile: function() {
            this.isMobile = (window.innerWidth <= 900) ? true : false;
        }
    },

    template: `
        <ul class="filters" :class="{mobile: isMobile}">

        <li>
            All
        </il>

        <li>
            Series
            <instrument-filters :list="data.seriesFilters"/>
        </li>

        <li>
            Body Shape
            <instrument-filters :list="data.bodyFilters"/>
        </li>

    </ul>
    `

});

Vue.component('instrument-filters', {

    props : [ 'list', ],

    methods: {
        toggle(toggleItem) {
          let itemInList = this.list.find((item) => item.value === toggleItem.value);        
        	itemInList.selected = !itemInList.selected;       
        },
    },

    template: `
    		<ul>
          <li v-for="item in list" :class="{ 'selected' : item.selected }" @click="toggle(item)">{{ item.label }}</li>
        </ul>
    `

});

new Vue({
  el: "#app",
  data: {
    filterData: {
      seriesFilters: [
        { label: 'All', value: 'All', selected: false },
        { label: 'Frontier', value: 'Frontier', selected: false },
        { label: 'Legacy', value: 'Legacy', selected: false },
        { label: 'USA', value: 'USA', selected: false },
      ],
      bodyFilters: [
        { label: 'All', value: 'All', selected: false },
        { label: 'Concert', value: 'Concert', selected: false },
        { label: 'Concertina', value: 'Concertina', selected: false },
        { label: 'Concerto', value: 'Concerto', selected: false },
        { label: 'Orchestra', value: 'Orchestra', selected: false },
      ],
    }
  },
});
ul {
 margin-left:20px; 
}
ul > li {
  cursor: pointer;
}
ul.filters > li > ul > li.selected::before {
   content: "✔️";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
   <header>
      <!-- desktop -->
      <guitar-filters :data="filterData" />
   </header>

   <!-- mobile -->
   <guitar-filters :data="filterData" />
</div>

fiddle :https://jsfiddle.net/nigelw/jpasfkxb

关于Vue.js:在页面中多次包含相同的组件实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54703207/

相关文章:

typescript - IntersectionObserver 在元素进入 View 之前触发

javascript - 如何在 vue.js 2 上获得 this.$store.dispatch 的响应?

javascript - Vue 相当于 setTimeout?

vue.js - 在 VueJS 的字符串中使用动态 ID

vuejs2 - 如何在 Nuxt 应用程序中使用 ag-grid

javascript - v-if 在我的子组件中不起作用

vue.js - Vue v-model 对 BS4 单选按钮组没有反应

javascript - VueJS - 我如何只有一个路由路径来加载具有不同 ID 的相同组件

javascript - 在 Vuetify 中折叠展开的 v-data-table 时触发方法

javascript - 我应该在 PWA 中的哪个位置加载一个较大的 JSON 文件?