Filter items list by template using ajax

Hi everyone :slight_smile:
Is there a cookbook or a famous thread that explains how to filter collection without reload the page? (Probably using ajax)

I saw that there is a recipe for load more but I wonder if there is something more specific for my purpose

Thanks in advance!

Maybe do it purely with CSS? https://webdesign.tutsplus.com/tutorials/how-to-build-a-filtering-component-in-pure-css--cms-33111

Or do something similar with JS, no need to use Ajax, if the stuff has been loaded anyway.

Unless you have pagination in place…

Thanks for the response
I do have pagination (load more) but I thought that I need to make the filtering before that…?

With pagination, filtering with CSS and JS will not work (or you would only filter the current pagination page, which doesn’t make sense).

So, with paginated pages, you would indeed have to filter with Ajax. Like in the load more recipe, create a content representation that returns the filtered results and replace the current elements with the elements from the response in your container.

1 Like

Thank you so much for the help
I decided to start with following this cookbook and create the pagination first and then to try to apply the filtering too but I stumble upon an error with the pagination after clicking on the ‘add more’ button:

madaf.js:559 GET http://localhost:8000/magazine.json/page:2 404 (Not Found)
Fetch error: SyntaxError: Unexpected token ‘<’, "<!doctype "… is not valid JSON

I did everything exactly as explained with only changing the classes and the variables names.
The other thing that is different is that I use 2 snippets:
I have a magazine page with header and footer and the magazine itself as a snippet on it’s own. and inside the magazine snippet I use anoth er snippet for the magazine-item. Maybe this causes the problem (?)

magazine pgae template:

<?php snippet('header') ?>

<?php snippet( "magazine" ); ?>
<button class="load-more" accesskey="m">Load more</button>

<?php snippet('footer') ?>

the magazine snippet:

  <div class="magazine magazine-items-json" id="magazine-items-container" data-href="magazine" data-page="<?= $pagination->nextPage() ?>">
        <?php foreach ($items as $item): ?>
            <?php snippet( "magazine_item", ['item'=>$item] ); ?>

        <?php endforeach ?>
    </div>

the magazine_item snippet:

<a href="<?=$item->url()?>" class="mag-item-link">
    <div class="mag-item">
        <figure class="mag-item-image">    
            <img src="<?= $item->cover()->toFiles() ?>" alt="">
        </figure>
        <p class="mag-item-quote"><?= $item->quote()?></p>
        <div class="mag-item-info">
            <h2 class="mag-item-title"><?= $item->title()?></h2>
            <?php if ( $item->intendedTemplate() == "recommend" ) : ?>
                <?php if ( $item->name()->isNotEmpty() ) : ?>
                <p class="mag-item-artist"><?= $item->name() ?></p>
                <?php endif; ?>
            <?php elseif ( $item->artist_name()->isNotEmpty() ) : ?>
            <p class="mag-item-artist"><?= $item->artist_name() ?></p>
            <?php endif; ?>
            <p class="mag-item-category"><?= $item->category() ?></p>
        </div>
    </div>
</a> 

magazine controller:

<?php

return function ( $page, $site, $kirby ) {

    $limit = 2;
    $items = page('magazine')->children()->listed()->paginate($limit);

    return [
        'limit'     => $limit,
        'items'     => $items,
        'pagination' => $items->pagination(),

    ];
};

magazine json in controllers:

<?php

return function ($page) {

    $limit = 2;
    $items = page('magazine')->children()->listed()->paginate($limit);
    $pagination = $items->pagination();
    $more       = $pagination->hasNextPage();

  return [
      'items'    => $items,
      'more'     => $more,
      'html'     => '',
      'json'     => [],
      'pagination' => $pagination,
    ];
};

magazine json in templates:

<?php

foreach($items as $item) {

  $html .= snippet('magazine_item', ['item' => $item], true);

}
$json['html'] = $html;
$json['more'] = $more;

echo json_encode($json);

js:

    if($body.hasClass('magazine-page')){
        const element = document.querySelector('.magazine-items-json');
        const button  = document.querySelector('.load-more');
        let page      = parseInt(element.getAttribute('data-page'));

        const fetchMagItems = async () => {
        let url = `${window.location.href.split('#')[0]}.json/page:${page}`;
        try {
            const response       = await fetch(url);
            const { html, more } = await response.json();
            button.hidden        = !more;
            element.insertAdjacentHTML('beforeend', html);
            page++;
        } catch (error) {
            console.log('Fetch error: ', error);
        }
        }

        button.addEventListener('click', fetchMagItems);
    }

Happily I found the problem when browsing the forum and found this thread

For now I have added another ‘:’ before the ${page} in the javascript file and now it works.
any idea why it happens? is it safe to keep going with this extra ‘:’?

Now I have a new problem:
after adding the pagination controls the images don’t load after the first page because it adds another “/page-name” to the url of the img:
localhost:8000/magazine/magazine/recommend-2/a4-book-mockup.jpeg

Why is it happening ? help ! :slight_smile:

Thanks!