Vue.js: Включение одного и того же экземпляра компонента несколько раз на странице - PullRequest
0 голосов
/ 15 февраля 2019

Что я пытаюсь сделать: у меня есть несколько фильтров, которые отображаются на странице для фильтрации продуктов, которые отображаются на странице.В мобильном устройстве я хочу скрыть эти фильтры за кнопкой, которая после нажатия на них покажет фильтры в выдвижном меню сбоку.

Хотя я могу дублировать одни и те же компоненты на странице дважды, компонентыэто не тот же самый экземпляр, то есть нажатие на фильтр вызовет эту функцию для фильтрации продуктов на странице, но оно устанавливает свои собственные атрибуты данных, которые я использую, чтобы сказать «если атрибут данных« выбран »является истинным,добавить «выбранный» класс к компоненту. Когда я изменяю размер окна, другой экземпляр компонента не имеет «выбранного» атрибута данных, помеченного как «истинный».

Я ожидаю этого, потому что изthe docs:

Обратите внимание, что при нажатии на кнопки каждая ведет свой отдельный подсчет, потому что каждый раз, когда вы используете компонент, создается его новый экземпляр.

... но как лучше всего это сделать?

Я поиграл с идеей просто установить класс 'mobile' на компонентent, и .mobile css будет стилизовать компоненты по-разному, но мне нужно, чтобы он вырвался туда, где он вложен.

например

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

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

Вот мой компонент Vue 'гитара-«Фильтры», который отображает несколько компонентов, называемых «инструмент-фильтр»:

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: "✔️";
   ...
}

цель состоит в том, чтобы фильтр имел класс selected в обоих случаях.Если я нажму на «фигуру концерта», а затем изменит размер окна на мобильную точку останова, будет выбран и другой экземпляр этого компонента фильтра.

РЕДАКТИРОВАТЬ: я мог бы взломать это.Я мог бы переместить один экземпляр компонента с помощью javascript, но я изучаю Vue и хочу сделать это способом Vue и лучшими практиками.

1 Ответ

0 голосов
/ 15 февраля 2019

Существует множество способов справиться с этим.Похоже, вы начали путь по шине событий.Другим вариантом может быть использование общего состояния приложения (см. Vuex ).

То, что я сделал, аналогично общему состоянию, но только с использованием приложения (то же самое относится и к общему родительскому компоненту).) данные.Общий объект передается в оба экземпляра компонента.Если элемент выбран, соответствующая запись переключается.Поскольку объект является общим, оба компонента остаются в синхронизации.

Если бы не было общего родительского компонента, вам нужно было бы посмотреть на события или состояние.

Посмотрите и посмотрите,помогает.

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>

Скрипка: https://jsfiddle.net/nigelw/jpasfkxb

...