如何在 svelte 中制作累积过滤系统(如亚马逊侧边栏)
How to make a cumulative filter system in svelte (like amazon sidebar)
我有一个显示 post 列表的简洁页面,我需要让用户能够按多种条件过滤该列表,例如:年份、标签、类别等。所有这些条件应该是并发的,就像亚马逊网站侧边栏一样。
使用派生商店,我的一个过滤器工作正常,但我仍然无法同时应用 2 或 3 个过滤器。
对于“年”过滤器,我有以下代码:
import { readable, writable, derived } from "svelte/store";
export let data;
let posts = readable(data.posts);
const extractColumn = (arr, column) => arr.map((x) => x[column]);
const allYears = extractColumn(data.posts, "year");
const years = readable ([...new Set(allYears)]);
const filter = writable({ year: null });
const filteredPosts = derived([filter, posts], ([$filter, $posts]) => {
if (!$filter.year) return $posts;
return $posts.filter((x) => x.year === $filter.year);
});
因此,基于 posts 列表,我可以获得过滤器的年份数组,然后使用简单的 select 触发过滤操作,这工作得很好。
<select bind:value={$filter.year}>
{#each $years as year}
<option value={year}>{year}</option>
{/each}
</select>
正如我在开头所说的,关键是如何为这个场景增加复杂性,将一个简单的过滤器变成一个系统,具有可变数量的动态累积过滤器。
感谢任何想法。
您可以使用 Object.entries
遍历对象的道具
将它与括号符号结合起来阅读 props obj[prop]
放入数组 reduce 以使其更简单,您将得到:
return Object.entries($filter)
.reduce(
(pre, [key, value]) => pre.filter(x => x[key] == value),
$posts
);
另外,您实际上可以将数组存储在过滤器中,这样您就可以过滤多个值:
{
"year": [2022],
"colour": ['red', 'green']
}
在这种情况下,请改用 pre.filter(x => values.includes(x[key]))
。
为了使其更加通用,我将从 'automatically' 生成过滤器选项开始
const filterOptions = derived(posts, $posts => {
// set the columns you'd like to have a filter for
const columns = ['year', 'language']
// const columns = Object.keys($posts[0]) // or change to this to have a filter for every property
return columns.reduce((filterOptions, column) => {
filterOptions[column] = extractUniqueColumnValues($posts, column)
return filterOptions
}, {})
})
function extractUniqueColumnValues (arr, column) {
return Array.from(new Set(arr.map((x) => x[column])))
}
并像这样显示它们
{#each Object.entries($filterOptions) as [column, values]}
{column}
<select bind:value={$filter[column]}>
<option value={null} ></option>
{#each values as value}
<option value={value}>{value}</option>
{/each}
</select>
{/each}
filter
可以简单地是
const filter = writable({});
(它最初通过 select 值绑定设置为 {year: null, language: null}
)
过滤帖子的一种方法可能是这个(或使用 reduce
,如 Stephane Vanraes 的答案所示)
const filteredPosts = derived([filter, posts], ([$filter, $posts]) => {
Object.entries($filter).forEach(([key, value]) => {
if(value !== null) $posts = $posts.filter(x => x[key] == value)
})
return $posts
});
这是在此基础上构建的 REPL
我有一个显示 post 列表的简洁页面,我需要让用户能够按多种条件过滤该列表,例如:年份、标签、类别等。所有这些条件应该是并发的,就像亚马逊网站侧边栏一样。
使用派生商店,我的一个过滤器工作正常,但我仍然无法同时应用 2 或 3 个过滤器。
对于“年”过滤器,我有以下代码:
import { readable, writable, derived } from "svelte/store";
export let data;
let posts = readable(data.posts);
const extractColumn = (arr, column) => arr.map((x) => x[column]);
const allYears = extractColumn(data.posts, "year");
const years = readable ([...new Set(allYears)]);
const filter = writable({ year: null });
const filteredPosts = derived([filter, posts], ([$filter, $posts]) => {
if (!$filter.year) return $posts;
return $posts.filter((x) => x.year === $filter.year);
});
因此,基于 posts 列表,我可以获得过滤器的年份数组,然后使用简单的 select 触发过滤操作,这工作得很好。
<select bind:value={$filter.year}>
{#each $years as year}
<option value={year}>{year}</option>
{/each}
</select>
正如我在开头所说的,关键是如何为这个场景增加复杂性,将一个简单的过滤器变成一个系统,具有可变数量的动态累积过滤器。
感谢任何想法。
您可以使用 Object.entries
遍历对象的道具
将它与括号符号结合起来阅读 props obj[prop]
放入数组 reduce 以使其更简单,您将得到:
return Object.entries($filter)
.reduce(
(pre, [key, value]) => pre.filter(x => x[key] == value),
$posts
);
另外,您实际上可以将数组存储在过滤器中,这样您就可以过滤多个值:
{
"year": [2022],
"colour": ['red', 'green']
}
在这种情况下,请改用 pre.filter(x => values.includes(x[key]))
。
为了使其更加通用,我将从 'automatically' 生成过滤器选项开始
const filterOptions = derived(posts, $posts => {
// set the columns you'd like to have a filter for
const columns = ['year', 'language']
// const columns = Object.keys($posts[0]) // or change to this to have a filter for every property
return columns.reduce((filterOptions, column) => {
filterOptions[column] = extractUniqueColumnValues($posts, column)
return filterOptions
}, {})
})
function extractUniqueColumnValues (arr, column) {
return Array.from(new Set(arr.map((x) => x[column])))
}
并像这样显示它们
{#each Object.entries($filterOptions) as [column, values]}
{column}
<select bind:value={$filter[column]}>
<option value={null} ></option>
{#each values as value}
<option value={value}>{value}</option>
{/each}
</select>
{/each}
filter
可以简单地是
const filter = writable({});
(它最初通过 select 值绑定设置为 {year: null, language: null}
)
过滤帖子的一种方法可能是这个(或使用 reduce
,如 Stephane Vanraes 的答案所示)
const filteredPosts = derived([filter, posts], ([$filter, $posts]) => {
Object.entries($filter).forEach(([key, value]) => {
if(value !== null) $posts = $posts.filter(x => x[key] == value)
})
return $posts
});
这是在此基础上构建的 REPL