Need regex filtering with angular-smart-table

1.1k Views Asked by At

I'm using angular smart table. Smart table has built in filtering using the st-search directive. However, I need some customized filtering using regular expressions.

I tried applying an angular filter to my data source (via the ng-repeat directive). While this works in regards to filtering, because smart table isn't aware of what i'm doing, it throws my paging off.

I've plunked an example of what is going on. (Try entering 1.8 in the account filter input box. You'll see the filter get applied but then if you click other pages, you'll see that they contain some of the filtered items as well.) I need the behavior to work similar to what happens if you filter by description (where filtered items get narrowed down and re-paged).

Here is my html table:

        <table st-table="vm.accounts" class="table table-striped table-hover table-condensed">
      <thead>
        <tr>
          <th st-sort="account">Account</th>
          <th st-sort="desc">Description</th>
          <th>Current Balance</th>
          <th> </th>
          <th> </th>
        </tr>
        <tr class="warning">
          <th>
            <input placeholder="filter accounts" class="form-control input-sm" type="search" ng-model="vm.accountfilter" />
          </th>
          <th>
            <input placeholder="filter description" class="form-control input-sm" type="search" st-search="desc" />
          </th>
          <th colspan="3"></th>
        </tr>
      </thead>
      <tbody>
        <tr ng-repeat="item in vm.accounts | filter:vm.myfilter" ng-click="vm.selected = item" ng-class="{info: vm.selected == item}">
          <td>{{item.account}}</td>
          <td>{{item.desc}}</td>
          <td>{{item.runbal | currency}}</td>
          <td><button class="btn btn-danger btn-xs"><i class="fa fa-times-circle"></i></button></td>
          <td><button class="btn btn-primary btn-xs"><i class="fa fa-pencil"></i></button></td>
        </tr>
      </tbody>
      <tfoot>
        <tr class="warning">
          <td colspan="5" class="text-center">
          <button class="btn btn-success btn-sm"><i class="fa fa-plus-circle"></i> Add new account</button>
          </td>
        </tr>
        <tr>
          <td colspan="5" class="text-center">
            <div st-pagination="" st-items-by-page="20" st-displayed-pages="10"></div>
          </td>
        </tr>
      </tfoot>
    </table>

and here is the filter that I'm trying to apply (from my controller):

(function(){
 'use strict';

 angular.module("myapp", ["smart-table"])
 .controller("mycontroller", MyController);

function MyController(){
var me = this;
me.accounts = [];
me.selected = null;
me.myfilter = myFilter;
me.accountfilter = '';

activate();

function activate(){
  for(var x = 0; x < 6000; x++)
  {
    var build = '';
    for(build; build.length < (12 / x.toString().length); build += x.toString()){}

    var aclass = Math.floor((1800 - 1100 + 1) * Math.random() + 1100).toString();
    var adept = Math.floor((800 - 100 + 1) * Math.random() + 100).toString();
    var aincex = Math.floor(1001 * Math.random() + 4000).toString();
    var asub = Math.floor(2 * Math.random());

    var account = aclass + adept + aincex + (asub ? "AB" + x.toString() : "");
    me.accounts.push({account: account, desc: "Text for " + x + " Account", runbal: x * 5, begbal: 5000, newbegbal: 20000, newrunbal: x * 7});
  }
}

function myFilter(value, index, array){
  if(!me.accountfilter) return true;

  var valex = new RegExp("^[*0-9a-zA-Z.]{1,22}$");
  if(me.accountfilter.match(valex))
  {
    var filter = me.accountfilter;
    debugger;
    filter = filter.replace(/\*/g,'\\w+');
    filter = "^" + filter + ".*$";
    var regex = new RegExp(filter,'i');
    return value.account.match(regex)
  }
  else
    return false;
}
}
})();

How can I "smart table enable" my filter?

1

There are 1 best solutions below

0
On BEST ANSWER

My suspicions were correct. I'm not sure if this is the best way to do it, but here is how I accomplished the special filtering.

I made 3 change to my html.

  1. I added st-pipe to my <table> element.

    <table st-pipe="vm.pipe" st-table="vm.accounts" ... >
    
  2. I removed the angular filter from my ng-repeat.

    <tr ng-repeat="item in vm.accounts" ... >
    
  3. I used the smart-table search feature on the account column (in place of the angular filter that I removed).

    <input st-search="account" ... />
    

In my controller I then added the following:

...
var internalList = [];
me.pipe = Pipe;

function Pipe(tableState){
  var perpage = tableState.pagination.number;
  var start = tableState.pagination.start;

  var filtered = internalList;

  //If user has entered filter criteria
  if(tableState.search.predicateObject)
  {
    //clone the filter criteria object
    var myPred = $.extend({},tableState.search.predicateObject);

    //remove the account criteria so I can process that myself
    delete myPred["account"];

    //perform the default filter function for any other filters
    filtered = $filter('filter')(filtered, myPred);

    //if user entered account (regex) filter then call my original filter function
    if(tableState.search.predicateObject["account"])
    {
      filtered = $filter('filter')(filtered,myFilter);
    }
  }

    //apply any sorting that needs to be applied
    if (tableState.sort.predicate) {
        filtered = $filter('orderBy')(filtered, tableState.sort.predicate, tableState.sort.reverse);
    }

  //set the bound array to the filtered contents
  me.accounts = filtered.slice(start, start +perpage);

  //clear the selected item if it is not included on the current page
  if(me.accounts.indexOf(me.selected) < 0)
    me.selected = null;

  //Set the proper number of pages for our filtered list
  tableState.pagination.numberOfPages = (filtered.length ? Math.floor(filtered.length / perpage) : 0);
}

Lastly, I had to change the activate function of my controller to populate the internalList rather than the display/bound list.

//me.accounts.push({account: account, desc: "Text for " + x + " Account", runbal: x * 5, begbal: 5000, newbegbal: 20000, newrunbal: x * 7});
internalList.push({account: ...});

You can see it in action here.