XF 2.3 Update quick_filter by link

stromb0li

Well-known member
In admin cp, I am using filter_macros -> quick_filter to filter a datalist. In the datalist, I want to generate a hyperlink that the user can click-on that immediately filters the datalist down to that one item.

Currently, I am generating a link like this: link('myaddon/mytable', [], {'_xfFilter[text]': $moo.cow}), however when I click on this, the page reloads to this link, but if there was an existing filter previously applied, the quick_filter textbox is prepopulated with the last search and covers up the explicit filter I was looking for.

Is there a way I can update the existing filter control via link instead or specify a parameter to ensure the previous filter is cleared?
 
Are the links on the same page as the filter? If so, you could set the input value via JS. Otherwise you could use a click handler to update the filter local storage item on click to populate your value as the "saved" filter.
 
Are the links on the same page as the filter? If so, you could set the input value via JS.
Yes, they are on the same page. I tried populating the input, but that doesn't seem to "trigger" the search.

Code:
<script>
    function updateSearchInput(searchText) {
        let input = document.querySelector('.js-filterInput');
        input.value = searchText;

        let event = new Event('input', { bubbles: true });
        input.dispatchEvent(event);
    }
</script>
<a href="#" onclick="updateSearchInput('{$moo.cow}');" title="{$moo.cow}">{$moo.cow}</a>

use a click handler to update the filter local storage item on click to populate your value as the "saved" filter.
Have an example of this?
 
It looks like it only listens to keyup/keydown/paste. You might try using the XF event methods to trigger a paste instead:

JavaScript:
XF.trigger(input, 'paste');
 
I may have misspoke. So by page, you mean the datalist paginated, not the same view?

This works in my dev environment where the datalist has a single "page". But in my production environment where the datalist does have multiple pages, it does fail:
Code:
Uncaught (in promise) TypeError: b.querySelectorAll is not a function
    at filter.min.js?_v=a35b164f:8:218
    at core-compiled.js?_v=a35b164f:49:446
    at Object.b [as load] (core-compiled.js?_v=a35b164f:105:231)
    at Object.setupHtmlInsert (core-compiled.js?_v=a35b164f:47:485)
    at b._filterAjaxResponse (filter.min.js?_v=a35b164f:8:133)
    at l (core-compiled.js?_v=a35b164f:38:141)
 
I just meant that the filter input is on the same page as the link. Not sure what would cause that error but it's potentially unrelated. If you can grab a stack trace with $config['development']['fullJs'] = true; enabled it might help track it down.
 
I tried setting that in my config.php file, reload the page, but don't see any additional details in the browser console (unless it's output somewhere else)? My assumption is it's failing because the ajax call is receiving back the full view with the <script> tags again.
 
You'd need regular development mode enabled as well. It should load unminified JS and present an unminified stack trace.

My assumption is it's failing because the ajax call is receiving back the full view with the <script> tags again.
Maybe, if you use <xf:js> instead of <script> then that kind of stuff should be handled automatically.
 
So I refactored to use part of the xf:js (originally didn't since the function wouldn't be found since XF wraps it gets wrapped with XF.ready(() =>{}) and that solved the problem.

This is what I have now, but is this the "proper"/most efficient way to implement this?
Code:
<xf:js>
    window.updateSearchInput = function (searchText) {
        let input = document.querySelector('.js-filterInput');
        input.value = searchText;
        // Trigger the search
        XF.trigger(input, 'paste');
    };
</xf:js>

I feel like the onclick implementation I have currently is a bit hacky, should I have this implement an onclick for classes that contain "mooSearch" and pull in a property value?
 
Last edited:
It's a little more fanfare, but canonically XF encapsulates pretty much all JS in "handler" objects in separate JS files. There are two main types of handlers, element handlers (initialized immediately via data-xf-init attributes) and event handlers (initialized when an event is fired via data-xf-{event} attributes).

You can create and use a click handler like this:
JavaScript:
window.SomeAddOn = window.SomeAddOn || {};

(() =>
{
    'use strict';

    SomeAddOn.SearchClick = XF.Event.newHandler(
    {
        eventNameSpace: 'SomeAddOnSearchClick',
        options: {
            searchText: '',
        },

        init ()
        {
            // code that runs when the handler is initialized
            // the corresponding element is available via `this.target`
        },

        click (e)
        {
            // code that runs when an element with this handler is clicked
            e.preventDefault();

            const input = document.querySelector('.js-filterInput');
            input.value = this.options.searchText;
            XF.trigger(input, 'paste');
        },
    });

    XF.Event.register('click', 'some-addon-search', 'SomeAddOn.SearchClick');
})();
HTML:
<a href="..." data-xf-click="some-addon-search" data-search-text="Some value">Some label</a>
 
Last edited:
Back
Top Bottom