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.

Love using ProcFu Guide?

Share your testimonial and let us know how ProcFu has helped you.

Share Your Testimonial

Built with ❤️ by Thaha for the Community