Simultaneously filtering list and map

Hi! A colleague and I are building a food donation app that connects food donations with local non-profits. It allows users to post either 1. a request for a certain kind of donation, or 2. leftover food they want to donate, and helps users find each other and make the donation happen as locally (and seamlessly) as possible.

We want users to be able to filter for donations or donation requests by location – namely, we want users to be able to filter a list-block that SIMULTANEOUSLY filters map data on the same page. That way, they can easily search by location. Is this possible?

We’ve tried finding out ways to do simultaneous list-block filtering with >1 list, but can’t find anything on it. However, our problem is specific to maps, as we basically want users to filter by a visible location. Any advice or feedback is super appreciated.

+1 We also have the same need. Ever figure this out?

1 Like

Not yet, but I’ll keep you in the loop on this thread.

1 Like

It can be done, via making custom filters as a global filter. Then lets say,

  1. On click on search, you can update the URL with the desired filter. List Filtering with URL – Softr Help Docs
  2. This will only work when you have same filter added on list and map.
  3. You can remove the filter block from the Map and List as well. There is some CSS files which you can mark as hidden.

Below is the code snippet.

I am getting dynamic dropdown values from the Airtable.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Filters with Airtable</title>
    <!-- Link to the Inter font for a modern look -->
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
    <style>
        /* General body styling */
        body {
            font-family: 'Inter', sans-serif;
            margin: 20px;
            background-color: #f4f4f4;
            color: #333;
        }
        /* Styling for the main heading */
        h1 {
            font-size: 24px;
            color: #007bff;
            text-align: left;
        }
        /* Container for filter elements */
        #filters {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
            margin-bottom: 20px;
            align-items: center;
        }
        /* Label styling for dropdowns */
        label {
            font-size: 18px;
            margin-bottom: 5px;
        }
        /* Dropdown styling */
        select {
            padding: 10px;
            border: 1px solid #007bff;
            border-radius: 5px;
            font-size: 16px;
            min-width: 200px;
        }
        /* Styling for the search button */
        button {
            background-color: #007bff;
            color: #fff;
            padding: 10px 20px;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            cursor: pointer;
            transition: background-color 0.3s;
            align-self: center;
        }
        /* Hover effect for the search button */
        button:hover {
            background-color: #0056b3;
        }
        /* Styling for results section */
        #results {
            margin-top: 20px;
            white-space: pre-wrap;
        }
    </style>
</head>
<body>

<!-- Main title for filtering section -->
<h1>Filter Apartments</h1>
<!-- Filters container for dropdowns and search button -->
<div id="filters">
    <!-- Label and dropdown for Bedrooms filter -->
    <label for="bedrooms">Bedrooms:</label>
    <select id="bedrooms">
        <option value="">Select Bedrooms</option>
        <!-- Dynamic options will be populated here -->
    </select>

    <!-- Label and dropdown for Pricing filter -->
    <label for="pricing">Price Range:</label>
    <select id="pricing">
        <option value="">Select Price Range</option>
        <!-- Dynamic options will be populated here -->
    </select>

    <!-- Search button to apply filters -->
    <button id="searchButton">Search</button>
</div>
<!-- Section to display results or generated URL -->
<div id="results">
    <!-- Filtered results will be displayed here -->
</div>

<!-- Include Axios for making API requests -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
    // Airtable API details
    const personalToken = 'KEY';
    const BASE_ID = 'BaseID';
    const TABLE_NAME = 'Tablename';
    const API_URL = `https://api.airtable.com/v0/${BASE_ID}/${TABLE_NAME}`;

    // Function to fetch data from Airtable
    const fetchDataFromAirtable = async () => {
        try {
            const response = await fetch(API_URL, {
                headers: {
                    'Authorization': `Bearer ${personalToken}`
                }
            });
            if (!response.ok) {
                throw new Error('Network response was not ok: ' + response.statusText);
            }
            const data = await response.json();
            return data.records;
        } catch (error) {
            console.error('Error fetching data from Airtable:', error);
            return [];
        }
    };

    // Function to get URL parameters
    function getUrlParam(name) {
        const url = new URL(window.location.href);
        return url.searchParams.get(name);
    }

    // Function to populate filter dropdowns
    const populateFilters = async () => {
        const data = await fetchDataFromAirtable();
        if (!data || !data.length) {
            console.error('No data returned or error in fetching data.');
            return;
        }

        const bedroomsSet = new Set();
        const pricingSet = new Set();
        
        // Extract unique values for Bedrooms and Pricing from Airtable data
        data.forEach(record => {
            const bedrooms = record.fields['Bedrooms'];
            const price = record.fields['Pricing'];  // Ensure 'Pricing' matches your Airtable field name

            if (bedrooms) bedroomsSet.add(bedrooms.trim().toUpperCase());
            if (price) pricingSet.add(price.trim());
        });

        // Sort and populate the dropdowns
        const bedroomsArray = Array.from(bedroomsSet).sort((a, b) => {
            const numA = parseInt(a.match(/\d+/));
            const numB = parseInt(b.match(/\d+/));
            return numA - numB;
        });
        
        const pricingArray = Array.from(pricingSet).sort((a, b) => parseFloat(a.replace(/[^0-9.]+/g, "")) - parseFloat(b.replace(/[^0-9.]+/g, "")));

        const bedroomsSelect = document.getElementById('bedrooms');
        const pricingSelect = document.getElementById('pricing');
        
        // Populate the Bedrooms dropdown
        bedroomsArray.forEach(bedrooms => {
            const optionElement = document.createElement('option');
            optionElement.value = encodeURIComponent(bedrooms);
            optionElement.textContent = bedrooms;
            bedroomsSelect.appendChild(optionElement);
        });

        // Populate the Pricing dropdown
        pricingArray.forEach(price => {
            const optionElement = document.createElement('option');
            optionElement.value = encodeURIComponent(price);
            optionElement.textContent = price;
            pricingSelect.appendChild(optionElement);
        });
    };

    // Function to set filter values from URL after page reload
    const setFilterValuesFromURL = () => {
        // Wait for 15 seconds to ensure Airtable data is fully loaded
        setTimeout(() => {
            // Get selected filter values from the URL using getUrlParam function
            const selectedBedrooms = getUrlParam('filter-by-Bedrooms');
            const selectedPrice = getUrlParam('filter-by-Price');

            // Get dropdown elements
            const bedroomsSelect = document.getElementById('bedrooms');
            const pricingSelect = document.getElementById('pricing');

            // Log the URL and fetched parameters
            console.log(`Current URL: ${window.location.href}`);
            console.log(`Bedrooms: ${selectedBedrooms}`);
            console.log(`Price: ${selectedPrice}`);

            // Set the selected values in the dropdowns if they exist
            if (selectedBedrooms) {
                bedroomsSelect.value = selectedBedrooms;
            }

            if (selectedPrice) {
                pricingSelect.value = selectedPrice;
            }
        }, 15000);  // Wait for 15000ms to ensure the elements are populated
    };

    // Function to apply filters and update the URL
    const applyFilters = () => {
        // Get selected values from the dropdowns
        const bedrooms = document.getElementById('bedrooms').value;
        const price = document.getElementById('pricing').value;

        // Construct the URL with the selected filters
        let url = `<URL>`; //Change URL

        if (bedrooms) {
            url += `filter-by-Bedrooms=${bedrooms}`;
        }

        if (price) {
            if (bedrooms) url += '&';  // Add '&' if Bedrooms filter is already in URL
            url += `filter-by-Price=${price}`;
        }

        // Update the window location to apply filters and refresh the page
        window.location.href = url;
    };

    // Initialize filters on page load
    window.onload = () => {
        // Populate filters from Airtable
        populateFilters().then(() => {
            // Add event listener to the search button
            document.getElementById('searchButton').addEventListener('click', applyFilters);

            // Set filter values from URL after a 15-second delay
            setFilterValuesFromURL();
        });
    };
</script>

</body>
</html>

For removing List and map filter: Put this in the header.

<style>
.css-tzsjye {
    display: none !important;
}

.css-bi377g {
    display: none !important;
</style>