Skip to content

Instantly share code, notes, and snippets.

@jonathanmach
Last active December 31, 2021 01:24
Show Gist options
  • Save jonathanmach/b016bacddcfcac9adceb3cac1e226e3c to your computer and use it in GitHub Desktop.
Save jonathanmach/b016bacddcfcac9adceb3cac1e226e3c to your computer and use it in GitHub Desktop.
Prototype: Advanced filtering: flexible conditions

Prototype: Advanced filtering - flexible conditions

🚧 Heads up: this is just a "throw-together" created without any attention to meaningful names, consistency and etc.

This prototype explores how to create a Vue.js UI component that allows users to create advanced filtering expressions. The code was created and can be run using Vue CLI Instant Prototype functionality.

The code is inspired on this Vue Tree View example.

The functionality is inspired on Notion's filter functionality.

Preview

image

<template>
<div>
<ul>
<template v-for="(filter, index) in filters.children">
<FilterExpression
:prop="filter"
:depth="1"
:index="index"
@add="addNewChild"
@change="mainChangeHandler"
@remove="remove"
:key="index"
></FilterExpression>
</template>
</ul>
<pre style="background: aliceblue;">{{ filters }}</pre>
</div>
</template>
<script>
import FilterExpression from './FilterExpression.vue'
export default {
components: { FilterExpression },
data() {
return {
filters: {
operator: 'OR',
children: [
{
text: 'expression 1',
},
{
operator: 'AND',
children: [
{
text: 'expression 2',
},
{
text: 'expression 2.1',
},
],
},
{
operator: 'AND',
children: [
{
operator: 'OR',
children: [
{
text: 'expression 3',
},
{
text: 'expression 3.1',
},
],
},
{
text: 'expression 4',
},
],
},
],
},
}
},
methods: {
mainChangeHandler(prop, event) {
console.log('mainChangeHandler >', prop, event.target.value)
prop.text = event.target.value
},
addNewChild(item) {
if (item.children) {
item.children.push({
text: 'New Child ',
})
} else {
this.$set(item, 'children', [
{
text: 'New Child ',
},
])
}
},
remove(list, index) {
console.log(list, index)
list.children.splice(index, 1)
},
},
}
</script>
<style lang="scss" scoped></style>
<template>
<div :style="`padding-left: ${depth * 5}px;`">
<!-- No nested data -->
<li v-if="!prop.children">
lvl{{ depth }}:
<input
@change="$emit('change', prop, $event)"
type="text"
name=""
id=""
:value="prop.text"
/>
<button @click="$emit('remove', $parent.prop, index)">remove</button>
</li>
<!-- if children, use recursion -->
<div v-else style="border: red solid 1px; padding: 2px;">
<template v-for="(child, index) in prop.children">
<child
@add="$emit('add', $event)"
@change="recursiveChangeHandler"
@remove="(parentList, index) => $emit('remove', parentList, index)"
:prop="child"
:depth="depth + 1"
:index="index"
:key="index"
></child>
</template>
<button @click="$emit('add', prop)">add</button>
</div>
<!-- else -->
</div>
</template>
<script>
export default {
name: 'child',
props: {
prop: {
type: Object,
required: true,
},
depth: {
type: Number,
required: true,
},
index: {
type: Number,
},
},
methods: {
recursiveChangeHandler(prop, event) {
// This is only responsible for re-emitting the event with all its arguments
// The same could be achieved by using @change="(prop, event) => $emit('change', prop, event)"
console.log('recursiveChangeHandler >', event.target.value)
this.$emit('change', prop, event)
},
},
}
</script>
<style lang="scss" scoped></style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment