Tips & Tricks
Custom filter with multi-select
App Builder & Automation Expert
Stay Updated with ProcFu!
Subscribe to our newsletter for the latest tips, updates, and automation insights.
Subscribe Now
Here's how you can create a multi-select filter like in an e-commerce website.
Setup
I have a Podio app called "Courses" with a list of courses. There are three category fields used for filtering: Audience, Type, and Topic. All of them are multi-select fields.
Summary Screen
The screen is set up as a Custom Filter screen, and I use the token @[pf_custom:filter]
in the
filter field.
Before Process
First, we need to pull data from the Podio app categories for Audience, Type, and Topic.
Next, we need to construct the filter.
my_variables.filter.audience = podio_app_field_get_categories(30104123,269768762)
my_variables.filter.type = podio_app_field_get_categories(30104123,269768763)
my_variables.filter.topic = podio_app_field_get_categories(30104123,269768764)
aud_arr = [];
if (!empty(url_parameters["audience"])) {
audience = explode(",", url_parameters["audience"]);
foreach (audience as audience_name) {
array_push(aud_arr, podio_app_field_get_category_id(30104123, 269768762, audience_name));
}
}
type_arr = [];
if (!empty(url_parameters["type"])) {
type = explode(",", url_parameters["type"]);
foreach (type as type_name) {
array_push(type_arr, podio_app_field_get_category_id(30104123, 269768763, type_name));
}
}
topic_arr = [];
if (!empty(url_parameters["topic"])) {
topic = explode(",", url_parameters["topic"]);
foreach (topic as topic_name) {
array_push(topic_arr, podio_app_field_get_category_id(30104123, 269768764, topic_name));
}
}
// Dynamically build the filters object
filters = [];
if (!empty(aud_arr)) {
filters['269768762'] = aud_arr;
}
if (!empty(type_arr)) {
filters['269768763'] = type_arr;
}
if (!empty(topic_arr)) {
filters['269768764'] = topic_arr;
}
// Set custom_tokens.filter
if (empty(filters)) {
custom_tokens.filter = '{}';
} else {
custom_tokens.filter = '{"filters":' + json_encode(filters) + '}';
}
On Render
Next, in the on-render event, we'll use JQuery to create the filter sidebar. The sidebar will have separate sections for Audience, Type, and Topic. Each section will include checkboxes based on the options retrieved earlier.
We will have an "Apply" button. When the user selects filters and clicks the "Apply" button, the page will dynamically construct a URL with the selected parameters. The user will be redirected to the updated URL with the selected filters applied.
let table = $('.pftable');
let wrapper = $('<div>', { class: 'filter-wrapper' });
table.parent().append(wrapper);
// Sidebar Container
let sidebar = $('<div>', { class: 'filter-sidebar' });
sidebar.append('<h3>Filters</h3>');
// Function to create checkbox groups
function createFilterGroup(title, filterData, filterKey) {
let group = $(''<div>', { class: 'filter-group' });
group.append(`'<h4'>${title}'</h4>`);
filterData.forEach(option => {
let checkbox = `
'<div>
'<input type="checkbox" name="${filterKey}" value="${option}" id="${filterKey}-${option}">
'<label for="${filterKey}-${option}">${option}'</label>
'</div>`;
group.append(checkbox);
});
return group;
}
// Inject filters into the sidebar
sidebar.append(createFilterGroup('Audience', my_variables.filter.audience, 'audience'));
sidebar.append(createFilterGroup('Type', my_variables.filter.type, 'type'));
sidebar.append(createFilterGroup('Topic', my_variables.filter.topic, 'topic'));
// Filter Button
let filterButton = $('<button>', { id: 'apply-filters', text: 'Apply' });
sidebar.append(filterButton);
// Append sidebar to the body (or a specific container)
wrapper.append(sidebar);
wrapper.append(table);
// Filter button click event
$('#apply-filters').click(function () {
let selectedFilters = {};
// Collect selected checkboxes
$('.filter-group input[type="checkbox"]:checked').each(function () {
let filterKey = $(this).attr('name');
let filterValue = $(this).val();
if (!selectedFilters[filterKey]) {
selectedFilters[filterKey] = [];
}
selectedFilters[filterKey].push(filterValue);
});
// Construct URL params
let queryParams = Object.keys(selectedFilters).map(key => `${key}=${selectedFilters[key].map(encodeURIComponent).join(',')}`).join('&');
let targetUrl = `?${queryParams}`;
// Redirect to the target page with filters
window.location.href = targetUrl;
});
// Parse URL parameters
const urlParams = new URLSearchParams(window.location.search);
// Iterate through each parameter and set checkboxes
urlParams.forEach((value, key) => {
if (value.includes(',')) {
// Handle multiple values (comma-separated)
value.split(',').forEach(val => {
$(`input[name="${key}"][value="${decodeURIComponent(val)}"]`).prop('checked', true);
});
} else {
// Handle single value
$(`input[name="${key}"][value="${decodeURIComponent(value)}"]`).prop('checked', true);
}
});
Styling (Header)
Let us add some styles.
<style>
@media (min-width: 769px) {
.filter-wrapper {
display: flex;
gap: 50px;
}
.filter-sidebar {
width: 250px;
}
.pfmcappwrapper {
max-width: 1200px;
}
}
</style>
End Result
The result is a simple and user-friendly filter system, dynamically linked to the backend, which can be easily reused in similar scenarios.