Как и в большинстве случаев при разработке программного обеспечения, обычно существует несколько способов реализации решения. Фильтрация данных, находящихся во Vuex, не является исключением. Это одна из возможных реализаций. В зависимости от вашей ситуации это может быть или не применимо. Для меня это сработало просто отлично.

Установка

У меня есть образец приложения под названием «Президенты».

Бьюсь об заклад, глядя на него, вы можете догадаться, что он делает. На нем изображены президенты США. Изысканный!

Для простоты у меня есть сервер Express, работающий в узле локально, и когда приложение загружает компонент App.vue, выполняется вызов Vuex для загрузки данных. Вот код из компонента приложения:

<script>
import HeaderComponent from './components/Header';
import { mapGetters } from 'vuex';
export default {
name: "app",
    components: { "header-component" : HeaderComponent },
    created() {
        this.$store.dispatch("getPresidents");
    }
};
</script>

В приведенном выше коде вы можете видеть, что при срабатывании ловушки created () в Vuex вызывается действие getPresidents ():

import axios from 'axios';
const state = {
    presidents: []
};
const actions = {
    getPresidents({commit}) {
        axios.get('/api/presidents').then((response) => {
            commit('UPDATE_PRESIDENTS', response.data);
        });
    }
};
const mutations = {
    UPDATE_PRESIDENTS(state, payload) {
        state.presidents = payload;
    }
};

В действии getPresidents () мы используем axios для выполнения HTTP-вызова и загрузки данных с сервера. Затем данные отображаются в компоненте PresidentList. Если мы подумаем о пользовательском интерфейсе с точки зрения компонентов, то вот как он выглядит:

У нас есть основной компонент приложения, обозначенный красным, компоненты header, filter и presidentList, обозначенные синим, и компонент PresidentListItem, обозначенный желтым.

В хранилище Vuex есть геттер, к которому обращается компонент PresidentList, и компонент PresidentListItem визуализируется для каждого президента.

Все идет нормально.

Обратим внимание на компонент фильтра.

Фильтрация в Vuex

Я хочу иметь возможность фильтровать список президентов по имени, партийной принадлежности или по обоим параметрам.

Моя первоначальная реализация передала две строки в хранилище Vuex, но мне на самом деле наплевать на эту настройку, поэтому я решил создать настраиваемый объект с именем filterObj.

filterObj упростит отслеживание критериев фильтрации. Мой настраиваемый объект также дал мне возможность легко увидеть, имею ли я дело с 1 или 2 критериями фильтрации.

Вот код настраиваемого filterObj.

const filterObj = {
    _presidentName: undefined,
    _partyAffiliation: undefined,
    _filterCount: 0
}
Object.defineProperty(filterObj, "presidentName", {
    enumerable: true,
    get(){
        return this._presidentName;
    },
    set(newVal) {
        if(newVal === ''){
            this._presidentName = undefined;
            this._filterCount--;
        } else {
            if(this._presidentName === undefined){
                this._filterCount++;
            }
            this._presidentName = newVal.toLowerCase();
        }
    }
});
Object.defineProperty(filterObj, "partyAffiliation", {
    enumerable: true,
    get(){
        return this._partyAffiliation;
    },
    set(newVal) {
        if(newVal === ''){
            this._partyAffiliation = undefined;
            this._filterCount--;
        } else {
            if(this._partyAffiliation === undefined){
                this._filterCount++;
            }
            this._partyAffiliation = newVal.toLowerCase();
        }
    }
});
Object.defineProperty(filterObj, "filterCount", {
    enumerable: true,
    get(){
        return this._filterCount;
    },
    set(newVal) {
        
        this._filterCount = newVal;
    }
});
export default filterObj;

Я реализовал это так по трем причинам:

  1. Я всегда хочу, чтобы передаваемое значение было в нижнем регистре, и это легко сделать в установщике для PresidentName.
  2. Я хочу иметь возможность сказать, со сколькими критериями фильтрации я имею дело, когда применяю фильтр. Причина этого в том, что если я имею дело только с 1 фильтром, я, по сути, выполняю логическое ИЛИ, тогда как если у меня есть 2 критерия, я выполняю логическое И.
  3. Просто красиво и легко передать объект со всей необходимой мне информацией

Всякий раз, когда пользователь взаимодействует с элементами управления компонентом Filter в пользовательском интерфейсе, все это синхронизируется с filterObj:

<template>
    <div>
        <div class="box">filter presidents <br>
            <label for="name">name: </label>
            <input type="text" class="input" v-model="filterObj.presidentName" @keyup="filterPresidents">
            
            <label for="name">party: </label>
            <select class="input" @change="filterPresidents" v-model="filterObj.partyAffiliation"> 
                <option  v-for="(party, key) in parties" :value="party" :key="key">{{ party }}</option>
            </select>
        </div>
    </div>
</template>
<script>
    import filterObj from "../utils/filterObj";
    export default {
        name: 'FilterPanel',
        data() {
            return {
                parties: ['','republican', 'democrat', 
                          'federalist', 'whig',       
                          'democratic-republican', 'none'],
                filterObj
            }
        },
        methods: {
            filterPresidents() {
                this.$store.dispatch('filterPresidents', 
                this.filterObj);
            }
        }
    }
</script>

Мы вызываем метод filterPresidents () для компонента всякий раз, когда данные в любом из элементов управления изменяются.

Одно из правил при работе с Vuex - мы никогда не изменяем состояние напрямую. Вместо этого мы отправляем в Действия в Магазине. Мы можем сделать это в методе filterPresidents (), передавая наш объект фильтра:

filterPresidents() {
       this.$store.dispatch('filterPresidents', 
       this.filterObj);
}

Оказавшись «внутри» хранилища, Действия Фиксируют мутации, которые видоизменяют состояние каким-либо образом:

const state = {
    presidents: [],
    filterObj: {}
};
const actions = {
    filterPresidents({commit}, filterObj) {
        commit('UPDATE_FILTER', filterObj);
}
};
const mutations = {
    UPDATE_FILTER(state, payload) {
        state.filterObj = payload;
    }
};
const getters = {
    presidents: state => state.presidents
}

После этого все, что осталось сделать, - это обновить наш получатель с простого, показанного выше, на тот, который распознает, что у нас есть filterObj в состоянии, и мы можем использовать его для динамической фильтрации:

const getters = {    
    presidents: state => {
        switch(state.filterObj.filterCount) {
            //no filters passed
            case 0:
            case undefined:
                return state.presidents;
//only 1 filter passed
            case 1:
                return state.presidents.filter(president => {
                    for(let key in state.filterObj) {
                        if(president[key]) {
                       if(president[key].toLowerCase().includes(state.filterObj[key]))
                                return true
                        }
                    }
                    return false;
                });
//2 filters passed
            case 2:
                return state.presidents.filter(president => {
                    for(let key in state.filterObj) {
                        if(president[key]) {
                            if(!president[key].toLowerCase().includes(state.filterObj[key]))
                                return false;
                        }
                    }
                    return true;
                });
        }
    }
};

Теперь мы можем легко фильтровать по количеству «фильтров», переданных в магазин.

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

Вы можете ознакомиться с полным исходным кодом на GitHub.

А пока спасибо за чтение и счастливого Vueing!

Первоначально опубликовано на http://ritzcovan.com.