Critical Issue: Repeater/Dataset Filter Not Updating on Same-Page URL Query Change (Header Search)

Hello Velo and Wix Studio Community,

I’m facing a complex and persistent challenge when filtering a repeater using URL query parameters coming from a Header search bar. I have exhausted standard Velo solutions and believe this is a deep-rooted Single Page Application (SPA) behavior issue.

:bullseye: The Problem

When a user is on the product listing page (/satilik-yatlar) and performs a second search using the global Header search bar:

  1. URL Updates Correctly: The URL successfully changes (e.g., from .../satilik-yatlar?searchQuery=YachtA to .../satilik-yatlar?searchQuery=YachtB).

  2. Filter Fails: The #dynamicDataset filter does not update, and the #repeater1 continues to display the results from the first search.

:brain: Analysis & Root Cause

The issue occurs because the search button click executes a wixLocation.to(newUrl) call from the Masterpage code. Since the URL path (/satilik-yatlar) remains the same, Wix performs a soft navigation (SPA transition) instead of a full page reload.

  • :cross_mark: The page’s $w.onReady function does not re-run.

  • :cross_mark: The wixLocation.onChange listener does not reliably trigger in the new Wix Studio/Velo environment (despite being the standard solution).

:hammer_and_wrench: Solutions Attempted (And Why They Failed)

I have tried the most common and advanced Velo solutions without success:

  1. Standard wixLocation.onChange: Implemented the filtering logic inside a reusable function and called it on wixLocation.onChange. Result: The listener rarely fires when only the query parameter changes on the same path.

  2. Header Code Relocation: Moved the search code out of the Masterpage and onto the Home page (or another page) to test. Result: The behavior remains the same, confirming it’s an SPA issue, not a scope issue.

  3. Aggressive Cache Control (Meta Tags): Tried adding aggressive no-cache meta tags to the page settings. Result: No change in Velo execution behavior.

  4. Forced Hard Refresh (using window.location.href): Attempted to use native browser navigation to force a full page reload. Result: Leads to continuous Velo Type Script/Linter errors (“Cannot find name ‘window’,” “Property ‘location’ does not exist,” etc.), making the code impossible to publish.

Here are my search bar and dynamic page codes

import wixLocation from 'wix-location';

$w.onReady(function () {
    $w("#button3").onClick(() => {
        const aramaTerimi = $w("#input7").value;

        if (aramaTerimi && aramaTerimi.trim().length > 0) {
            const encodedTerm = encodeURIComponent(aramaTerimi.trim());
            
            // Yeni arama sorgusu ile yönlendir
            wixLocation.to(`/satilik-yatlar?searchQuery=${encodedTerm}`);
        } else {
            // Arama terimi yoksa sadece liste sayfasına yönlendir
            wixLocation.to(`/satilik-yatlar`);
        }
    });
});
import wixLocation from 'wix-location';
import wixData from 'wix-data';

// Filtreleme mantığı, sadece URL'den gelen query objesiyle çalışır.
function applyFilter(query) {
    const dataset = $w("#dynamicDataset"); 
    const repeaterId = "#repeater1";

    // Görsel atlamayı önlemek için repeater'ı her zaman önce gizle
    $w(repeaterId).hide(); 
    dataset.setFilter(wixData.filter()); // Önceki filtreleri temizle

    let filter = wixData.filter();
    let shouldFilter = false;
    
    // Dataset hazır olduğunda filtreyi uygula
    dataset.onReady(() => {

        // --- 1. ÖNCELİK: Tekli Arama Çubuğu Filtresi (yat_adi) ---
        if (query.searchQuery) {
            
            const aramaTerimi = query.searchQuery;
            filter = filter.contains("yat_adi", aramaTerimi);
            shouldFilter = true;
            
        } 
        // --- 2. İKİNCİL ÖNCELİK: Çoklu Filtreleme Sistemi ---
        else { 
            // Dropdown Filtreleri
            if (query.tekne_tipi) {
                filter = filter.eq("tekne_tipi", query.tekne_tipi); shouldFilter = true;
            }
            if (query.marka) {
                filter = filter.eq("marka", query.marka); shouldFilter = true;
            }

            // Sayısal Alanlar (Yıl, Fiyat, Boy)
            if (query.minYl && !isNaN(parseInt(query.minYl))) { filter = filter.ge("yl", parseInt(query.minYl)); shouldFilter = true; }
            if (query.maxYl && !isNaN(parseInt(query.maxYl))) { filter = filter.le("yl", parseInt(query.maxYl)); shouldFilter = true; }
            if (query.minFiyat && !isNaN(parseInt(query.minFiyat))) { filter = filter.ge("fiyat", parseInt(query.minFiyat)); shouldFilter = true; }
            if (query.maxFiyat && !isNaN(parseInt(query.maxFiyat))) { filter = filter.le("fiyat", parseInt(query.maxFiyat)); shouldFilter = true; }
            if (query.minBoy && !isNaN(parseFloat(query.minBoy))) { filter = filter.ge("boy", parseFloat(query.minBoy)); shouldFilter = true; }
            if (query.maxBoy && !isNaN(parseFloat(query.maxBoy))) { filter = filter.le("boy", parseFloat(query.maxBoy)); shouldFilter = true; }
        }

        // --- 3. FİLTREYİ UYGULA VE GÖSTER ---
        if (shouldFilter) {
            dataset.setFilter(filter)
                .then(() => {
                    if (dataset.getTotalCount() > 0) {
                        $w(repeaterId).show();
                    }
                })
                .catch((error) => {
                    console.error("Filtreleme hatası:", error);
                });
        } else {
            // Hiçbir parametre yoksa, tüm listeyi göster
            dataset.setFilter(wixData.filter())
                .then(() => {
                    $w(repeaterId).show();
                });
        }
    }); // dataset.onReady sonu
}


// ANA BLOK: Sayfa yüklendiğinde çalışır
$w.onReady(function () {
    
    // Filtreyi doğrudan URL'deki mevcut query ile uyguluyoruz.
    applyFilter(wixLocation.query);
    
    // NOT: wixLocation.onChange kalıcı olarak kaldırıldı.
});

Need help! Thanks!

You’re setting the dataset filter within its onReady
That means, once it gets filtered, it fires onReady, which filters it again, resulting in an infinite loop
Maybe it’s a good thing it hadn’t filtered even once

It’s a bit late so I can’t go over and help revise the code right now, but come tomorrow, if you still need help, I’ll try and help organize what’s going on there

In the meantime, a few questions:

  • Do you ever head over to this page (from another page) with a filter already in query? If not, then the entire query thing may be unnecessary, as all filters can be handled by the page logic using memory only, and it’d be a lot simpler without having to encode all filter data into URI
  • Do you ever call applyFilter aside from within $w.onReady? Perhaps the logic shouldn’t be separated into a new method
1 Like