Search code examples
javascriptjquerylodashlistjs

Filtering a property with multiple values for one field - List.js and Filter.js


I am currently using the list.js plugin along with it's filter extension to produce a search results page that allows the user to filter down the end results to make it easier for them to find exactly what they are looking for.

I have been using their API to try and come up with a solution but in all honesty it is a little dated and not sure when it was last updated.

http://www.listjs.com/docs/list-api

My code is as follows:

HTML

<div id="search-results">
  <div class="col-md-3">
    <div class="panel panel-warning">
      <div class="panel-heading">Filters</div>
      <div class="panel-body">
        <div class="search-filter">
          <ul class="list-group">
            <li class="list-group-item">
              <div class="list-group-item-heading">
                <h4>Filter Options</h4>
              </div>
            </li>
            <li class="list-group-item">
              <div class="nameContainer">
                <h5 class="list-group-item-heading">Name</h5>
              </div>
            </li>
            <li class="list-group-item">
              <div class="typeContainer">
                <h5 class="list-group-item-heading">Type</h5>
              </div>
            </li>
            <li class="list-group-item">
              <div class="difficultyContainer">
                <h5 class="list-group-item-heading">Difficulty</h5>
              </div>
            </li>
            <li class="list-group-item">
              <label>Tour contains</label>
              <input class="search form-control" placeholder="Search" />
            </li>
          </ul>
        </div>
      </div>
    </div>
  </div>
  <div class="col-md-9">
    <div class="panel panel-primary">
      <div class="panel-heading">Results</div>
      <div class="list panel-body">
         <div class="package well">
           <div class="name">Niagra Falls</div>
           <div class="type hidden">Boat Trip</div>
           <div class="difficulty">Relaxed</div>
         </div>
        <div class="package well">
           <div class="name">Pyramids</div>
           <div class="type hidden">History Holiday</div>
           <div class="difficulty">Relaxed</div>
         </div>
        <div class="package well">
           <div class="name">Great Barrier Reef</div>
           <div class="type hidden">Snorkling Holiday</div>
           <div class="difficulty">Dangerous</div>
         </div>
        <div class="package well">
           <div class="name">Boar Hunting</div>
           <div class="type hidden">Hunting Trip</div>
           <div class="difficulty">Active</div>
         </div>
        <div class="package well">
           <div class="name">Thames Cruise</div>
           <div class="type hidden">Cruise</div>
           <div class="difficulty">Easy</div>
         </div>
      </div>
      <ul class="pagination"></ul>
    </div>
  </div>
</div>

Javascript

var options = {
            valueNames: ['name', 'type', 'difficulty'],
            page: 3,
            plugins: [
               ListPagination({})
            ]
        };
        var userList = new List('search-results', options);
        var updateList = function () {
            var name = new Array();
            var type = new Array();
            var difficulty = new Array();

            $("input:checkbox[name=name]:checked").each(function () {
                name.push($(this).val());
            });

            $("input:checkbox[name=type]:checked").each(function () {
                type.push($(this).val());
            });

            $("input:checkbox[name=difficulty]:checked").each(function () {
                difficulty.push($(this).val());
            });

            var values_type = type.length > 0 ? type : null;
            var values_name = name.length > 0 ? name : null;
            var values_difficulty = difficulty.length > 0 ? difficulty : null;

            userList.filter(function (item) {
                return (_(values_type).contains(item.values().type) || !values_type)
                        && (_(values_name).contains(item.values().name) || !values_name)
                        && (_(values_difficulty).contains(item.values().difficulty) || !values_difficulty)
            });
        }

        userList.on("updated", function () {
            $('.sort').each(function () {
                if ($(this).hasClass("asc")) {
                    $(this).find(".fa").addClass("fa-sort-alpha-asc").removeClass("fa-sort-alpha-desc").show();
                } else if ($(this).hasClass("desc")) {
                    $(this).find(".fa").addClass("fa-sort-alpha-desc").removeClass("fa-sort-alpha-asc").show();
                } else {
                    $(this).find(".fa").hide();
                }
            });
        });

        var all_type = [];
        var all_name = [];
        var all_difficulty = [];

        updateList();
        _(userList.items).each(function (item) {
            all_type.push(item.values().type)
            all_name.push(item.values().name)
            all_difficulty.push(item.values().difficulty)
        });

        _(all_type).uniq().each(function (item) {
            $(".typeContainer").append('<label><input type="checkbox" name="type" value="' + item + '">' + item + '</label>')
        });

        _(all_name).uniq().each(function (item) {
            $(".nameContainer").append('<label><input type="checkbox" name="name" value="' + item + '">' + item + '</label>')
        });

        _(all_difficulty).uniq().each(function (item) {
            $(".difficultyContainer").append('<label><input type="checkbox" name="difficulty" value="' + item + '">' + item + '</label>')
        });

        $(document).off("change", "input:checkbox[name=type]");
        $(document).on("change", "input:checkbox[name=type]", updateList);
        $(document).off("change", "input:checkbox[name=name]");
        $(document).on("change", "input:checkbox[name=name]", updateList);
        $(document).off("change", "input:checkbox[name=difficulty]");
        $(document).on("change", "input:checkbox[name=difficulty]", updateList);

I've also created a working example on Codepen.

http://codepen.io/JasonEspin/pen/bdajKo

What I wish to achieve is for some packages, they may have multiple type values such as:

 <div class="package well">
       <div class="name">Niagra Falls</div>
       <div class="type hidden">Boat Trip</div>
       <div class="type hidden">Other trip type</div>
       <div class="difficulty">Relaxed</div>
 </div>

So in this situation, I would expect my filter to detect that there is a type of Boat Trip and Other trip type and display these options as a filter option. If either is selected, this package is then returned. However, it seems to ignore the second type.

I have even tried it like this as I expected it to act like an array but this was not the case. It just mashed the two items together to create a random option.

 <div class="package well">
       <div class="name">Niagra Falls</div>
       <div class="type hidden"><div>Boat Trip</div><div>Other Trip Type</div> </div>
       <div class="difficulty">Relaxed</div>
 </div>

So, does anyone have any ideas how I can adapt my code to accept multiple options? My ideal scenario would be for me to attach a number of departure dates to each package and enable the user to filter by these departure dates.

Any help would be greatly appreciated as I believe the issue may be with my Lodash code but as it is my first time using Lodash i'm a little bit unsure of what it is actually doing due to its unusual syntax.


Solution

  • This was actually fairly straightforward to implement using a combination of string.split() definitions and array concatenations.

    HTML

    <div id="search-results">
      <div class="col-md-3">
        <div class="panel panel-warning">
          <div class="panel-heading">Filters</div>
          <div class="panel-body">
            <div class="search-filter">
              <ul class="list-group">
                <li class="list-group-item">
                  <div class="list-group-item-heading">
                    <h4>Filter Options</h4>
                  </div>
                </li>
                <li class="list-group-item">
                  <div class="nameContainer">
                    <h5 class="list-group-item-heading">Name</h5>
                  </div>
                </li>
                <li class="list-group-item">
                  <div class="typeContainer">
                    <h5 class="list-group-item-heading">Type</h5>
                  </div>
                </li>
                <li class="list-group-item">
                  <div class="difficultyContainer">
                    <h5 class="list-group-item-heading">Difficulty</h5>
                  </div>
                </li>
                <li class="list-group-item">
                  <label>Tour contains</label>
                  <input class="search form-control" placeholder="Search" />
                </li>
              </ul>
            </div>
          </div>
        </div>
      </div>
      <div class="col-md-9">
        <div class="panel panel-primary">
          <div class="panel-heading">Results</div>
          <div class="list panel-body">
             <div class="package well">
               <div class="name">Niagra Falls</div>
               <div class="type hidden">Boat Trip|Other Trip|My Trip</div>
               <div class="difficulty">Relaxed</div>
             </div>
            <div class="package well">
               <div class="name">Pyramids</div>
               <div class="type hidden">History Holiday</div>
               <div class="difficulty">Relaxed</div>
             </div>
            <div class="package well">
               <div class="name">Great Barrier Reef</div>
               <div class="type hidden">Snorkling Holiday</div>
               <div class="difficulty">Dangerous</div>
             </div>
            <div class="package well">
               <div class="name">Boar Hunting</div>
               <div class="type hidden">Hunting Trip</div>
               <div class="difficulty">Active</div>
             </div>
            <div class="package well">
               <div class="name">Thames Cruise</div>
               <div class="type hidden">Cruise</div>
               <div class="difficulty">Easy</div>
             </div>
          </div>
          <ul class="pagination"></ul>
        </div>
      </div>
    </div>
    

    JAVASCRIPT

    var options = {
            valueNames: ['name', 'type', 'difficulty'],
            page: 3,
            plugins: [
               ListPagination({})
            ]
        };
        var userList = new List('search-results', options);
        var updateList = function () {
            var name = new Array();
            var type = new Array();
            var difficulty = new Array();
    
            $("input:checkbox[name=name]:checked").each(function () {
                name.push($(this).val());
            });
    
            $("input:checkbox[name=type]:checked").each(function () {
        if($(this).val().indexOf('|') > 0){
           var arr = $(this).val().split('|');
           var arrayLength = arr.length;
           type = type.concat(arr);
           console.log('Multiple values:' + arr);
        }else{
           type.push($(this).val());
           console.log('Single values:' + arr);
        }
            });
    
            $("input:checkbox[name=difficulty]:checked").each(function () {
                difficulty.push($(this).val());
            });
    
            var values_type = type.length > 0 ? type : null;
            var values_name = name.length > 0 ? name : null;
            var values_difficulty = difficulty.length > 0 ? difficulty : null;
    
            userList.filter(function (item) {
        var typeTest;
        var nameTest;
        var difficultyTest;
    
        if(item.values().type.indexOf('|') > 0){
          var typeArr = item.values().type.split('|');
          for(var i = 0; i < typeArr.length; i++){
             if(_(values_type).contains(typeArr[i])){
                typeTest = true;   
             }
          }
        }
    
                return (_(values_type).contains(item.values().type) || !values_type || typeTest)
                        && (_(values_name).contains(item.values().name) || !values_name)
                        && (_(values_difficulty).contains(item.values().difficulty) || !values_difficulty)
            });
        }
    
        userList.on("updated", function () {
            $('.sort').each(function () {
                if ($(this).hasClass("asc")) {
                    $(this).find(".fa").addClass("fa-sort-alpha-asc").removeClass("fa-sort-alpha-desc").show();
                } else if ($(this).hasClass("desc")) {
                    $(this).find(".fa").addClass("fa-sort-alpha-desc").removeClass("fa-sort-alpha-asc").show();
                } else {
                    $(this).find(".fa").hide();
                }
            });
        });
    
        var all_type = [];
        var all_name = [];
        var all_difficulty = [];
    
        updateList();
    
        _(userList.items).each(function (item) {
      if(item.values().type.indexOf('|') > 0){
        var arr = item.values().type.split('|');
        all_type = all_type.concat(arr);
      }else{
        all_type.push(item.values().type)
      }
    
            all_name.push(item.values().name)
            all_difficulty.push(item.values().difficulty)
        });
    
        _(all_type).uniq().each(function (item) {
            $(".typeContainer").append('<label><input type="checkbox" name="type" value="' + item + '">' + item + '</label>')
        });
    
        _(all_name).uniq().each(function (item) {
            $(".nameContainer").append('<label><input type="checkbox" name="name" value="' + item + '">' + item + '</label>')
        });
    
        _(all_difficulty).uniq().each(function (item) {
            $(".difficultyContainer").append('<label><input type="checkbox" name="difficulty" value="' + item + '">' + item + '</label>')
        });
    
        $(document).off("change", "input:checkbox[name=type]");
        $(document).on("change", "input:checkbox[name=type]", updateList);
        $(document).off("change", "input:checkbox[name=name]");
        $(document).on("change", "input:checkbox[name=name]", updateList);
        $(document).off("change", "input:checkbox[name=difficulty]");
        $(document).on("change", "input:checkbox[name=difficulty]", updateList);
    

    Codepen

    http://codepen.io/JasonEspin/pen/bdajKo