Как и в большинстве случаев при разработке программного обеспечения, обычно существует несколько способов реализации решения. Фильтрация данных, находящихся во 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;
Я реализовал это так по трем причинам:
- Я всегда хочу, чтобы передаваемое значение было в нижнем регистре, и это легко сделать в установщике для PresidentName.
- Я хочу иметь возможность сказать, со сколькими критериями фильтрации я имею дело, когда применяю фильтр. Причина этого в том, что если я имею дело только с 1 фильтром, я, по сути, выполняю логическое ИЛИ, тогда как если у меня есть 2 критерия, я выполняю логическое И.
- Просто красиво и легко передать объект со всей необходимой мне информацией
Всякий раз, когда пользователь взаимодействует с элементами управления компонентом 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.