0

Solution

Sergio's answer below worked great, specifically the objectsMatch function he provided. I also need to support multi-select select elements for filtering so I modified his function a bit, here it is in case anyone else is looking for similar functionality:

function objectsMatch(roll, filterObject) {
  var match = true;
  for (var prop in filterObject) {
    if(!roll.hasOwnProperty(prop) ||
       !filterObject.hasOwnProperty(prop)) continue;
       if(Array.isArray(filterObject[prop])){
         var matchesNoValues = function(){
           for(var i = 0; i < filterObject[prop].length; i++){
             console.log(filterObject[prop][i])
             if(roll[prop] == filterObject[prop][i]){
               return false;
             }
           }
           return true;
         }
           if (matchesNoValues()  &&
                filterObject[prop] != ''
               ) {
               match = false;
               }
       }else{
         if (roll[prop] != filterObject[prop]  &&
                filterObject[prop] != ''
             ) {
             match = false;
             }
       }
    }
  return match;
}

EDIT 1

I was able to replicate the functionality I'm looking for in an MVP here. And am now trying to figure out how to apply that to my filter. Hopefully this can better demonstrate what I'm trying to achieve.

Original Post

I am attempting to add a custom filter for filtering rows in a table in Angular. I need to check whether each object in my repeat has all the values represented in my filters array. These values are set by select elements in my table.

For example, my data set looks like:

$scope.sushi = [
  { name: 'Cali Roll', fish: 'Crab', tastiness: 2 },
  { name: 'Philly', fish: 'Tuna', tastiness: 4 },
  { name: 'Tiger', fish: 'Eel', tastiness: 7 },
  { name: 'Tiger', fish: 'Tuna', tastiness: 3 },
  { name: 'Rainbow', fish: 'Variety', tastiness: 62 }
];

And say my filters array contains:

var filters = ['Philly', 'Tuna', 4];

it will only show

$scope.sushi[1] 

in the repeat.

Then say the filters array values are updated to:

var filters = ['Philly', 'Tuna', 1000];

Now no rows would display because there is no object in my dataset that contains all the values in the filters array.

HERE is a link to what I've tried so far, with both methods marked METHOD 1 and METHOD 2, respectively.

I think I'm almost there with METHOD 1, but if I select multiple values that are in the object, no rows get displayed, it only displays a row when I only select 1 filter. As for METHOD 2, its only checking that at least one of the values in the filters array are in the object, not all.

Here is the full code in my filter:

.filter('byColumn', function(){
  return function(rolls, filterVal){
  var filtered = [];
  var test = true;
  for(var x in rolls){
    var currentRoll = rolls[x];
    for(y in currentRoll){

    // METHOD 1
    var allMatches = true;
    for(var i = 0; i < filterVal.length; i++){
      if(filterVal[i]){
        if(currentRoll[y] == filterVal[i]){
          continue;
        }else{
          allMatches = false;
          break;
        }
      }
    }
    if(allMatches){
        if (filtered.indexOf(currentRoll) == -1) {
          filtered.push(currentRoll);
        }
    }
    if(!allMatches){
      rolls = [];
    }

    // METHOD 2
    // angular.forEach(filterVal, function(filter){
    //   if(currentRoll[y] == filter){
    //     if (filtered.indexOf(currentRoll) == -1) {
    //         filtered.push(currentRoll);
    //     }
    //   }
    // });

      }
    }
        return filtered.length ? filtered : rolls;
      }
    });

The code is based off a tutorial by Scotch.io found here that I have modified to fit my needs.

Thanks in advance for any suggestions.

1 Answer 1

1

You could pass to the byColumn filter the columnFilter object instead of the filters array

will look like this:

<tr ng-repeat="roll in sushi | orderBy:sortType:sortReverse | filter:searchFish | byColumn:columnFilter track by $index">
  <td>{{ roll.name }}</td>
  <td>{{ roll.fish }}</td>
  <td>{{ roll.tastiness }}</td>
</tr>

byColumn filter could look like this:

.filter('byColumn', function(){
    return function(rolls, filterVal){

   var filtered = [];

   for(var x in rolls){
      var currentRoll = rolls[x];

      if(objectsMatch(currentRoll, filterObject)) {
          filtered.push(currentRoll);
      }
   }

   return filtered;
  };
})

And this is the objectsMatch function

function objectsMatch(roll, filterObject) {

    var match = true;

    for (var prop in filterObject) {

        if(!roll.hasOwnProperty(prop) ||
           !filterObject.hasOwnProperty(prop)) continue;

        if (roll[prop] != filterObject[prop] && filterObject[prop] != '') 
        {
            match = false;
        }
    }

    return match;
}

Fiddle Here

Hope this works for you!

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for taking a look @Sergio, but it doesn't seem like the fiddle is working? As soon as I make a selection all the rows disappear?
I thought that was the desired behavior. Only rows that match the three filters will be shown
Sorry I guess I wasn't clear in my initial post. What I am looking for is if there is a filter, it only shows rows that contain that value. If there are 2 filters in the array, it only shows rows that have BOTH those values. If there are 3 filters in the array, it must contain all 3 values in that row, etc. I added a link to an MVP fiddle that better demonstrates what I'm looking for
@adrian_reimer I updated the answer. Hope it helps now

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.