Filter project-list by multiple custom fields

thanks, I sent her my project. Let’s see…

Don’t you think it would be more efficient to implement a non php filtering solution right away? Reloading the page for every filter isn’t very user friendly. Sonja suggested Isotope and Shuffle.js in her second post and I think she is right.

PS This is a project from the kirby showcase page. I really love their filtering solution: http://laurenwickware.com/#/?view=filter&tagged=award
It’s a different approach but gives a very good overview.

I haven’t received anything …?

Edit: If you use AJAX, you avoid page reloading; the javascript solutions are not necessarily easier to implement, though, and add additional page weight to your site.

That’s strange… I’ll send you a link. Thanks![quote=“texnixe, post:22, topic:3946, full:true”]
I haven’t received anything …?

FYI: I deleted my license from config.php and replaced it with the invoice number for verification.

Yeah, I just downloaded it, trying to figure out why I get a blank page. I’ll get back to you ASAP.

Ah, ok, found it, an empty home template.

Ok, I eliminated the issues I found, this is the new filter form, see comments

<form id="filters" action="" method="post">
//the select field needs a name
  <select name="status" onchange="this.form.submit()">
   //have a first option that asks to select an option
    <option selected value="">Select a status</option>

    <?php foreach($status as $item): ?>
     //filter out empty items
      <?php if ($item == "") continue; ?>
       //set the option to selected if selected
      <option <?php if((isset($_POST['status'])) && $_POST['status'] == $item) { echo "selected"; } ?> value="<?php echo $item->html() ?>"><?php echo $item->html() ?></option>
    <?php endforeach ?>
  </select>
   <select name="year" onchange="this.form.submit()">
     <option selected value="">Select a year</option>
    <?php foreach($year as $item): ?>
      <?php if ($item == "") continue; ?>
      <option <?php if((isset($_POST['year'])) && $_POST['year'] == $item) { echo "selected"; } ?> value="<?php echo $item->html() ?>"><?php echo $item->html() ?></option>
    <?php endforeach ?>
  </select>
</form>

Oh my dear it works!!! Thank you so much, please let me at least buy you a Beer / Smoothie / Hot Chocolate / Chai Latte or whatever you’re into!

1 Like

You are welcome :slight_smile:.

Hi texnixe, it’s me again. Here is a working solution based on jQuery. It’s a mix of your code with a nice plugin called multifilter.js (I know the script is not supposed to be anywhere near the template but hey : )

  <script src="https://code.jquery.com/jquery-2.0.3.js"></script>
    <script>(function($) {
  "use strict";
  $.fn.multifilter = function(options) {
    var settings = $.extend( {
      'target'        : $('table'),
      'method'    : 'thead' // This can be thead or class
    }, options);

    jQuery.expr[":"].Equals = function(a, i, m) {
      return (a.textContent || a.innerText || "").toUpperCase() === m[3].toUpperCase();
    };

    this.each(function() {
      var $this = $(this);
      var container = settings.target;
      var row_tag = 'tr';
      var item_tag = 'td';
      var rows = container.find($(row_tag));

      if (settings.method === 'thead') {
        // Match the data-col attribute to the text in the thead
        var col = container.find('th:Equals(' + $this.data('col') + ')');
        var col_index = container.find($('thead th')).index(col);
      };

      if (settings.method === 'class') {
        // Match the data-col attribute to the class on each column
        var col = rows.first().find('td.' + $this.data('col'));
        var col_index = rows.first().find('td').index(col);
      };

      $this.change(function() {
        var filter = $this.val();
        rows.each(function() {
          var row = $(this);
          var cell = $(row.children(item_tag)[col_index]);
          if (filter) {
            if (cell.text().toLowerCase().indexOf(filter.toLowerCase()) !== -1) {
              cell.attr('data-filtered', 'positive');
            } else {
              cell.attr('data-filtered', 'negative');
            }
            if (row.find(item_tag + "[data-filtered=negative]").size() > 0) {
               row.hide();
            } else {
              if (row.find(item_tag + "[data-filtered=positive]").size() > 0) {
                row.show();
              }
            }
          } else {
            cell.attr('data-filtered', 'positive');
            if (row.find(item_tag + "[data-filtered=negative]").size() > 0) {
              row.hide();
            } else {
              if (row.find(item_tag + "[data-filtered=positive]").size() > 0) {
                row.show();
              }
            }
          }
        });
        return false;
      }).click(function() {
        $this.change();
      });
    });
  };
})(jQuery);</script>
    
    <link href='style.css' media='screen' rel='stylesheet' type='text/css' />
  </head>
  <script type='text/javascript'>
    //<![CDATA[
      $(document).ready(function() {
        $('.filter').multifilter()
      })
    //]]>
  </script>
  <body>
 





<?php

// main menu items

$status = page('projects')->children()->pluck('status', null, true);
$year = page('projects')->children()->pluck('year', null, true);

// only show the menu if items are available


?>
<form id="filters" action="" method="post">

  <select name="status" class='filter'  data-col='status'/>

    <option selected value="">Select a status</option>

    <?php foreach($status as $item): ?>
     //filter out empty items
      <?php if ($item == "") continue; ?>
       //set the option to selected if selected
      <option <?php if((isset($_POST['status'])) && $_POST['status'] == $item) { echo "selected"; } ?> value="<?php echo $item->html() ?>"><?php echo $item->html() ?></option>
    <?php endforeach ?>
  </select>
   <select name="year" class='filter' data-col='year'/>
     <option selected value="">Select a year</option>
    <?php foreach($year as $item): ?>
      <?php if ($item == "") continue; ?>
      <option <?php if((isset($_POST['year'])) && $_POST['year'] == $item) { echo "selected"; } ?> value="<?php echo $item->html() ?>"><?php echo $item->html() ?></option>
    <?php endforeach ?>
  </select>
</form>


<div class='container'>
  <table>
    <thead>
    <th>title</th>
      <th>status</th>
      <th>year</th>
      <th>cat</th>
    </thead>
    <tbody> <?php foreach(page('projects')->children()->visible() as $project): ?>
      <tr>
        <td>
          <?php echo $project->id000()->html() ?> <a href="<?php echo $project->url() ?>"><?php echo $project->title()->html() ?></a>
        </td>
        <td>
          <?php echo $project->status()->html() ?>
        </td>
        <td>
          <?php echo $project->year()->html() ?>
        </td>
        <td>
          <?php echo $project->cat()->html() ?>
        </td>
      </tr> <?php endforeach ?>
    </tbody>
  </table>
</div>
1 Like

Thanks for sharing! :slight_smile:

Hi @RDR,

Is there a way you can share how ended up the Blueprint, Controller and Template? I´m also very new in Kirby and I´m on a project that needs this kind of filters.

Any info would be appreciated.

Best,

1 Like

The above example is complete, it does not need a controller, because the filtering is done with javascript. As regards the blueprint, all you need is the fields you want to filter by, in the example above that’s “year” and “status”.

In the docs you can find an example without javascript, with a form and the corresponding controller.

Hi @texnixe

Hypothetical question, is there a way to integrate this with a param function?
For instance if we went to the url /category:drawing it would enable the page to bring up the results as if ‘drawing’ had been selected using the dropdown form?

I know its a shot in the dark, really appreciate your expertise!

Yes, of course you can filter by URL parameter, you can find an example in the tags cookbook recipe.

I’ll give it a go, thank you!

@1-L-P Keep in mind that if you want to filter by multiple fields/params (that was the question in this thread), it makes more sense to use select fields.

Yes indeed as it’s currently not working for me. based on the java and selected fields how would the filter be set up with the param function? Apologies for the bloated code, I’m still just trying things out. the idea would be if I used the url say category:design it would auto select that in the form to bring up the results… based on the code in this thread. Hope this makes sense.

Thanks (as always)

I must admit that I’m not quite following you…

Guess you mean JavaScript, Java is a completely different programming language…

Code??

Oh I’m reffering to the example in this thread Here’s what I am playing around with:

    <script src="https://code.jquery.com/jquery-2.0.3.js"></script>
    <script>(function($) {
      "use strict";
      $.fn.multifilter = function(options) {
    var settings = $.extend( {
      'target'        : $('table'),
      'method'    : 'thead' // This can be thead or class
    }, options);

    jQuery.expr[":"].Equals = function(a, i, m) {
      return (a.textContent || a.innerText || "").toUpperCase() === m[3].toUpperCase();
    };

    this.each(function() {
      var $this = $(this);
      var container = settings.target;
      var row_tag = 'tr';
      var item_tag = 'td';
      var rows = container.find($(row_tag));

      if (settings.method === 'thead') {
        // Match the data-col attribute to the text in the thead
        var col = container.find('th:Equals(' + $this.data('col') + ')');
        var col_index = container.find($('thead th')).index(col);
      };

      if (settings.method === 'class') {
        // Match the data-col attribute to the class on each column
        var col = rows.first().find('td.' + $this.data('col'));
        var col_index = rows.first().find('td').index(col);
      };

      $this.change(function() {
        var filter = $this.val();
        rows.each(function() {
          var row = $(this);
          var cell = $(row.children(item_tag)[col_index]);
          if (filter) {
            if (cell.text().toLowerCase().indexOf(filter.toLowerCase()) !== -1) {
              cell.attr('data-filtered', 'positive');
            } else {
              cell.attr('data-filtered', 'negative');
            }
            if (row.find(item_tag + "[data-filtered=negative]").size() > 0) {
               row.hide();
            } else {
              if (row.find(item_tag + "[data-filtered=positive]").size() > 0) {
                row.show();
              }
            }
          } else {
            cell.attr('data-filtered', 'positive');
            if (row.find(item_tag + "[data-filtered=negative]").size() > 0) {
              row.hide();
            } else {
              if (row.find(item_tag + "[data-filtered=positive]").size() > 0) {
                row.show();
              }
            }
          }
        });
        return false;
      }).click(function() {
        $this.change();
      });
    });
  };
})(jQuery);</script>



    <link href='style.css' media='screen' rel='stylesheet' type='text/css' />
  </head>
  <script type='text/javascript'>
    //<![CDATA[
      $(document).ready(function() {
        $('.filter').multifilter()
      })
    //]]>
  </script>
  <body>



<main class="main" role="main">
<?php

// main menu items

$category = page('projects')->children()->pluck('category', null, true);
$type = page('projects')->children()->pluck('type', null, true);
$tags = page('projects')->children()->pluck('tags', null, true);
$year = page('projects')->children()->pluck('year', null, true);
$tags2 = page('projects')->children()->pluck('tags2', null, true);

// only show the menu if items are available


?>
<form id="filters" action="" method="post">

  <select name="category" class='filter'  data-col='category'/>

    <option selected value="">Select category</option>

    <?php foreach($category as $item): ?>
     //filter out empty items
      <?php if ($item == "") continue; ?>
       //set the option to selected if selected
      <option <?php if((isset($_POST['category'])) && $_POST['category'] == $item) { echo "selected"; } ?> value="<?php echo $item->html() ?>"><?php echo $item->html() ?></option>
    <?php endforeach ?>
  </select>

  <select name="type" class='filter' data-col='type'/>
    <option selected value="">Select type</option>
   <?php foreach($type as $item): ?>
     <?php if ($item == "") continue; ?>
     <option <?php if((isset($_POST['type'])) && $_POST['type'] == $item) { echo "selected"; } ?> value="<?php echo $item->html() ?>"><?php echo $item->html() ?></option>
   <?php endforeach ?>
 </select>


  <select name="theme" class='filter' data-col='theme'/>
     <option selected value="">Select a theme</option>
    <?php foreach($tags as $item): ?>
      <?php if ($item == "") continue; ?>
      <option <?php if((isset($_POST['tags'])) && $_POST['tags'] == $item) { echo "selected"; } ?> value="<?php echo $item->html() ?>"><?php echo $item->html() ?></option>
    <?php endforeach ?>

    <?php foreach($tags2 as $item): ?>
        <option <?php if((isset($_POST['tags2'])) && $_POST['tags2'] == $item) { echo "selected"; } ?> value="<?php echo $item->html() ?>"><?php echo $item->html() ?></option>
<?php endforeach ?>
  </select>
</form>


<div class='container-fluid'>
  <table style="width: 100%;">
    <thead>
      <th class="title subheading">Title</th>
      <th class="category subheading">Category</th>
      <th class="type subheading">Type</th>
      <th class="theme subheading">Theme</th>
      <th class="year subheading">Year</th>
    </thead>
    <tbody><?php foreach(page('projects')->children()->visible() as $project): ?>
      <tr>
        <td class="title">
          <?php echo $project->id000()->html() ?> <a href="<?php echo $project->url() ?>"><?php echo $project->title()->html() ?><?php echo $project->image()->html() ?></a>
        </td>
        <td class="category">
          <?php echo $project->category()->html() ?>
        </td>
        <td class="type">
          <?php echo $project->type()->html() ?>
        </td>
        <td class="theme">
          <?php echo $project->tags()->html() ?>
          <?php echo $project->tags2()->html() ?>
        </td>
        <td class="year">
          <?php echo $project->year()->html() ?>
        </td>
      </tr> <?php endforeach ?>
    </tbody>
  </table>
</div>
</main>

Sorry, maybe a misunderstanding, but I don’t get your use case. Why do you need URL params on top of the what is done here?

Just sent through a use case to your inbox. thanks.