3

I learn best by having a "project" to work on. I'm learning angular 1.x by making a golf course listing app. golf app I have a json file I'm using with all the data. This app has a general text search, and 8 switches one can set to filter the region of the state and the type of golf course. I've got text search working great. I hacked up some of that code to make a single filter for "course type" and it actually works. But as soon as I try to make a second filter it breaks. I'll post my code below, but it is probably hacky.

What's the best way to put together EIGHT true/false switches into a filter or filters, and also combine that with a text search? I figure filter first, then text search the filtered results.

The html ng-repeat (if I take out the 2nd "private" filter, the public one works:

<div ng-repeat="course in items | searchFor:data.searchString | orderBy: 'name' | publicFilter:data.publicCourse | privateFilter:data.privateCourse " >

The filters (first one works by itself):

 .filter('publicFilter', function(){

      return function(arr, publicCourse){
           var result = [];
                if(publicCourse == true ){
                     angular.forEach(arr, function(item){
                         if(item.coursetype.toLowerCase().indexOf('public') !== -1){result.push(item);}
                     });
                     return result;
                };
      }
 })

 .filter('privateFilter', function(){

      return function(arr, privateCourse){
           var result = [];
                if(privateCourse == true ){
                     angular.forEach(arr, function(item){
                         if(item.coursetype.toLowerCase().indexOf('private') !== -1){result.push(item);}
                     });
                     return result;
                };
      }
 })
mediaguru
  • 1,807
  • 18
  • 24
  • In your `publicFilter` and `privateFilter`, you are not returning anything when condition is not satisfied. is that expected behavior? – Prasanth Bendra Feb 21 '17 at 05:50
  • Well Parasanth I'm not sure. I suppose it is possible to turn off all filters and have no results. I expect the user to have at least one coursetype and one region selected however. Forcing one to be on all the time is possible, but a discussion for another question. – mediaguru Feb 21 '17 at 14:55

3 Answers3

1

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

app.controller('mainController', function($scope) {
    // Data object
    $scope.courses = [
        {name:'Course 1', courseType:'Public', region:'Northern'},
        {name:'Course 2', courseType:'Public', region:'Northern'},
        {name:'Course 3', courseType:'Private', region:'Northern'},
        {name:'Links 4', courseType:'Private', regionmode:'Northern'},
        {name:'Links 5', courseType:'Private', region:'Northern'},
        {name:'Links 6', courseType:'Public', region:'Southern'},
        {name:'Links 7', courseType:'Public', region:'Southern'},
        {name:'Links 8', courseType:'Military', region:'Southern'},
        {name:'Course 9', courseType:'Private', region:'Southern'},
        {name:'Course 10', courseType:'Private', region:'Southern'}
    ];
    // Filter defaults
    $scope.Filter = new Object();
    $scope.Filter.courseType = {'public':'public',
                            'private':'private',
                            'military': 'military'
                        };
    $scope.Filter.region = {'northern':'northern',
                            'southern':'southern'
                        };
    // Default order
    $scope.OrderFilter = 'region';
});

// Global search filter
app.filter('searchFilter',function($filter) {
        return function(items,searchfilter) {
             var isSearchFilterEmpty = true;
              angular.forEach(searchfilter, function(searchstring) {   
                  if(searchstring !=null && searchstring !=""){
                      isSearchFilterEmpty= false;
                  }
              });
        if(!isSearchFilterEmpty){
                var result = [];  
                angular.forEach(items, function(item) {  
                    var isFound = false;
                     angular.forEach(item, function(term,key) {                         
                         if(term != null &&  !isFound){
                             term = term.toString();
                             term = term.toLowerCase();
                                angular.forEach(searchfilter, function(searchstring) {      
                                    searchstring = searchstring.toLowerCase();
                                    if(searchstring !="" && term.indexOf(searchstring) !=-1 && !isFound){
                                       result.push(item);
                                        isFound = true;
                                    }
                                });
                         }
                            });
                       });
            return result;
        }else{
        return items;
        }
    }
});
body{
    font-family:calibri,arial;
    line-height: 1em;
}
h2{
    font-size:14pt;
    font-weight:bold;
}
li{
    font-family:courier;
}

th{
    font-weight:bold;
    cursor:pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
    <div  ng-controller="mainController">
    <label>Search: <input ng-model="searchText"></label>
        <h2>Course Type</h2>
        <label>Public</label>
        <input type="checkbox" ng-model="Filter.courseType.public" ng-true-value="public"  ng-false-value="!public" />&nbsp;
        <label>Private</label>
        <input type="checkbox" ng-model="Filter.courseType.private" ng-true-value="private"  ng-false-value="!private" />&nbsp;
        <label>Military</label>
        <input type="checkbox" ng-model="Filter.courseType.military" ng-true-value="military"  ng-false-value="!military" />&nbsp;
        <hr />
        <h2>Region</h2>
        <label>Northern</label>
        <input  type="checkbox" ng-model="Filter.region.northern" ng-true-value="northern"  ng-false-value="!northern" />&nbsp;
        <label>Southern</label>
        <input  type="checkbox" ng-model="Filter.region.southern" ng-true-value="southern"  ng-false-value="!southern" />
        <hr />
        <h2>Results:</h2>
        <table width="100%" cellpadding="5">
          <thead>
            <tr style="text-align:left">
                <th ng-click="OrderFilter='name'">Name</th>
                <th ng-click="OrderFilter='courseType'">Course Type</th>
                <th ng-click="OrderFilter='region'">Region</th>
            </tr>
          </thead>
          <tbody>
            <tr ng-repeat="course in courses | filter:searchText | searchFilter:Filter.name | searchFilter:Filter.courseType | searchFilter:Filter.region | orderBy:OrderFilter">
                <td>{{course.name}}</td>
                <td>{{course.courseType}}</td>
                <td>{{course.region}}</td>
            </tr>
          </tbody>
        </table>
    </div>
</div>

Here's a jsfiddle of the above: http://jsfiddle.net/j6cgovjh/2/

Disclosure: this code was based on reworking someone else's jsfiddle - http://jsfiddle.net/w01edye9/ (I added a search box amongst other minor changes).

K Scandrett
  • 16,390
  • 4
  • 40
  • 65
  • Wow K Scandrett thanks very much. I'm about to run to work but it looks like your code does the trick. I will look deeper this evening and mark as answered. – mediaguru Feb 21 '17 at 15:02
  • K Scandrett: The arrays in the filters are a little different. Not sure this will matter. So for instance "region" is the key and there are four different values. The same with course type. courseType can have four different values in the array. – mediaguru Feb 21 '17 at 15:10
  • The filter object is only there to set the initial values for the check boxes to be true, otherwise the filter will filter out everything initially. The essential data is in `$scope.courses` - where region is the key with multiple values – K Scandrett Feb 21 '17 at 21:43
  • `regionmode:'Northern'` is a typo. Should be `region:'Northern'` – K Scandrett Feb 21 '17 at 21:45
  • Angular 1.5.x Expected constant expression for `ngTrueValue`, but saw `northern`. Expected constant expression for `ngTrueValue`, but saw `private`. – mediaguru Feb 22 '17 at 02:45
  • K Scandrett I posted my angular 1.5.5 fix. Now I have to figure out how to bind angular material switches (pictured) to the models. The catch with those is that they return true or false. Sigh. – mediaguru Feb 22 '17 at 03:27
  • If you find that presents a problem pose a new question and send me the link. I'll try to answer it. – K Scandrett Feb 22 '17 at 03:35
  • Ok @k-scandrett http://stackoverflow.com/questions/42407837/angular-material-md-switch-and-search-input-in-one-controller-to-another-control – mediaguru Feb 23 '17 at 05:34
0

In angular 1.5.5 I had to format the input true and false values with single quotes inside of double quotes for it to work:

 <input type="checkbox" ng-model="Filter.courseType.public" ng-true-value="'public'" ng-false-value="'!public'" />
mediaguru
  • 1,807
  • 18
  • 24
0

I have not tried with ng-repeat but for ng-options in select this works fine for multiple inline filters:

<select
        name="benefitDeductibleType"
        ng-model="vm.report.BenefitDeductibleType"
        ng-options="type.Id as type.Name for type in vm.benefitDeductibleTypes | filter: (vm.report.HoursRut <= 0 ? {Id: '!1'} : '') | filter: (vm.report.HoursRot <= 0 ? {Id: '!2'} : '')"
</select>

The current code filters out Id 1 or Id 2 based on the condition.

Ogglas
  • 62,132
  • 37
  • 328
  • 418