Norgul
Norgul

Reputation: 4783

AngularJS multiple filter

I have a list of orders and a dropwdown list to filter orders depending on their status. Statuses are: not delivered, fully delivered and partially delivered. Filtering works fine with those 3 options, however I would like to implement one extra option that shows both not delivered and partially delivered orders together.

<select class="form-control" id="status" ng-model="orderFilter.status"
        ng-init="orderFilter.status = '_'">
    <option value="_">all</option>
    <option value="_not" class="text-danger">not delivered</option>
    <option value="_part" class="text-info">partially delivered</option>
    <option value="n-p" class="text-warning">not and partial</option>
    <option value="_done" class="text-success">delivered</option>
</select>

So I've added a new "custom" value, and the way it all works is, it is making copy of objects which are either one or the other, and that is bad, redundant and not what I want.

I thought it may be possible something like:

<option value="_not || _part" class="text-warning">not and partial</option>

Filtering part:

<tr ng-repeat="s in vm.sales | filter: orderFilter">
    <td>...</td>
</tr>

Upvotes: 0

Views: 64

Answers (2)

Jaydo
Jaydo

Reputation: 1859

If your possible statuses aren't going to change you could use !_done as your option value which would filter out the _done but leave _not and _part.

var app = angular.module("app", []);

app.controller("controller", function($scope) {
  $scope.orderFilter = {
    status: "_"
  };

  $scope.sales = [{
      status: "_not",
      item: "NOT"
    },
    {
      status: "_part",
      item: "PART"
    },
    {
      status: "_done",
      item: "DONE"
    }
  ];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.1/angular.min.js"></script>
<div ng-app="app" ng-controller="controller">
  <select class="form-control" id="status" ng-model="orderFilter.status">
    <option value="_">all</option>
    <option value="_not" class="text-danger">not delivered</option>
    <option value="_part" class="text-info">partially delivered</option>
    <option value="!_done" class="text-warning">not and partial</option>
    <option value="_done" class="text-success">delivered</option>
  </select> {{orderFilter}}

  <table>
    <tbody>
      <tr ng-repeat="s in sales | filter: orderFilter.status">
        <td>{{s.item}}</td>
      </tr>
    </tbody>
  </table>
</div>

Otherwise you'd need to write a custom filter. See the snippet below which let's you add multiple values e.g. "_not, _part" would return both not and partially delivered items.

var app = angular.module("app", []);

app.controller("controller", function($scope) {
  $scope.orderFilter = {
    status: ""
  };

  $scope.sales = [{
      status: "_not",
      item: "NOT"
    },
    {
      status: "_part",
      item: "PART"
    },
    {
      status: "_done",
      item: "DONE"
    }
  ];
});

app.filter("filterByProp", function() {
  return function(items, prop, filterVal) {
    // don't filter if no value to filter on
    if (!filterVal) {
      return items;
    }
    
    // split filter val to allow for multiple props e.g. "_not, _part"
    var filters = filterVal.replace(" ", "").split(",");
    
    // only return items which match at least one of the values
    return items.filter(function(item) { 
      return filters.indexOf(item[prop]) > -1 
    });
  };
});;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.1/angular.min.js"></script>
<div ng-app="app" ng-controller="controller">
  <select class="form-control" id="status" ng-model="orderFilter.status">
    <option value="">all</option>
    <option value="_not" class="text-danger">not delivered</option>
    <option value="_part" class="text-info">partially delivered</option>
    <option value="_not, _part" class="text-warning">not and partial</option>
    <option value="_done" class="text-success">delivered</option>
  </select> {{orderFilter}}

  <table>
    <tbody>
      <tr ng-repeat="s in sales | filterByProp : 'status' : orderFilter.status">
        <td>{{s.item}}</td>
      </tr>
    </tbody>
  </table>
</div>

Upvotes: 0

Sheki
Sheki

Reputation: 1635

The best solution is to write your own filter, if you don't want to do that for some reason, than you can filter the list programmaticaly and updating the list depending on selected value:

<select class="form-control" ng-change="filterSales()" id="status" ng-model="orderFilter.status"
    ng-init="orderFilter.status = '_'">
    <option value="_">all</option>
    <option value="_not" class="text-danger">not delivered</option>
    <option value="_part" class="text-info">partially delivered</option>
    <option value="n-p" class="text-warning">not and partial</option>
    <option value="_done" class="text-success">delivered</option>
</select>

And than in your controller somewhere:

$scope.filterSales = function(){
   if($scope.orderFilter.status === 'n-p'){
      var filteredWithNoStatus = $filter('filter')($scope.vm.sales,'_not');
      var filteredWithPartStatus = $filter('filter')($scope.vm.sales,'_part');
      $scope.filteredList =  Object.assign(filteredWithNoStatus,filteredWithPartStatus);
   }else{
       $scope.filteredList = $filter('filter')($scope.vm.sales,$scope.orderFilter.status);
   }
}

In your html you can remove than the filter

<tr ng-repeat="s in filteredList>
   <td>...</td>
</tr>

Sorry that I didn't had the time to test the code, but I hope you understand the Idea.

Upvotes: 1

Related Questions