5

I've got a list of invoices in ng-table and would like to be able to filter on a nested attribute. The json looks like this;

[
  {
     id: 1,
     date: "20/03/2014",
     no: "1",
     client: {
       fullname: "ABC Catering"
     }
  }
]

My view look like this

<table ng-table="tableParams" show-filter="true" class="table">
  <tr class='listing' ng-repeat="invoice in $data">
    <td data-title="'Invoice No.'" sortable="'no'" filter="{'no':'text'}">
      {{invoice.no}}
    </td>
    <td data-title="'Date'" sortable="'date'" filter="{'date':'text'}">
      {{invoice.date}}
    </td>
    <td data-title="'Client'" sortable="'client.fullname'" filter="{'client.fullname':'text'}">
      {{invoice.client.fullname}}
    </td>
    <td>
      <a href="/api#/invoices/{{invoice.id}}">Show</a>
      <a href="/api#/invoices/{{invoice.id}}/edit">Edit</a>
      <a href="" ng-confirm-click="destroy(invoice.id)">Delete</a>
    </td>
  </tr>
</table>

I would like to get the filtering working for client.fullname. How do I filter on nested attributes?

Update

I've found a work around where I just put the nested field into a non-nested JSON element, in the above example I create a JSON['client_name'] element and assign it to client.fullname within the rails model. Then the filter works as it's not nested.

Still looking for a way in which I could do without this work around.

map7
  • 5,096
  • 6
  • 65
  • 128
  • 1
    One way (without using `ng-table`) would be to supply a custom comparator for [filter](http://docs.angularjs.org/api/ng/filter/filter), such that it compares based on your `client.fullname` field. Here's a quick and dirty example I hacked up based on your supplied code - http://jsfiddle.net/hRdu4/ – miqh Mar 20 '14 at 06:10
  • can you post your `tableParams`? following the example of filtering on the ngtable webpage, filtering on nested attributes seems to work fine – j.wittwer Apr 08 '14 at 04:13

4 Answers4

7

You can use $filter on anything you want to filter on from the JSON response.

HERE is a contrived example of how filtering can be done on nested JSON element. Sample code is taken from one of the example of ng-table's usage with filters.

Main part to note in the app is

$scope.tableParams = new ngTableParams({
    page: 1,
    count: 10,
    filter: {
      'client': 'Bo' //Sample filter
    }
  }, {
    total: data.length,
    getData: function($defer, params) {

      //Note the usage of built in angular filter
      //which is injected in the app

      var orderedData = 
                params.filter() ?
                     $filter('filter')(data, params.filter()) :
                        data;

      $scope.users = 
          orderedData.slice((params.page() - 1) * params.count(), 
                             params.page() * params.count());

      params.total(orderedData.length);
      $defer.resolve($scope.users);
    }
  });

Plunker works as expected (if I got your requirement correct). Give a shout if that something that is not what you are aiming at. :)

dmahapatro
  • 49,365
  • 7
  • 88
  • 117
  • 1
    you beat me to it. i was also able to use the filter example with nested attributes. http://plnkr.co/1yvDy3 – j.wittwer Apr 08 '14 at 04:11
  • 1
    @j.wittwer your snippet is awesome but it's not working with Angular V1.33. (if you clear full name filter the data doesn't appear anymore) http://plnkr.co/edit/N3ucerZQLatLvEHez72Q?p=preview – Walid Ammar Nov 24 '14 at 12:56
  • There is an issue indeed with angular 1.3. See https://github.com/esvit/ng-table/issues/388#issuecomment-60019509 for details and a possible workaround. – Hendrik Dec 02 '14 at 15:56
  • @Hendrik Thanks. Can you come with a sample using the workaround? Will be glad if you add a new answer or can edit my answer with your update. :) – dmahapatro Dec 02 '14 at 16:20
  • @dmahapatro Hey friend, I wondered if I have the following json: [ { id: 1, date: "20/03/2014", no: "1", client: { fullname: "ABC Catering", book:{ title:"One Book" } } } { id: 2, date: "20/03/2014", no: "11", client: { fullname: "ABC Catering", books:{ title:"Snow Book" } } } ] And I wanna filter by title. How could I? – andreszam24 Jul 12 '16 at 22:44
2

Just stumbled upon this problem and wasn't really satisfied with any of the solutions so here's my try ... It's just about overriding the filters before we get to the actual filtering.

getData: function ($defer, params) {

    var filters = params.filter();
    var newFilters = {};

    for (var key in filters) {
        if (filters.hasOwnProperty(key)) {
            switch(key) {
                case 'client.fullname':
                    angular.extend(newFilters, {
                        client: {
                            fullname: filters[key]
                        }
                    });
                    break;
                default:
                    newFilters[key] = filters[key];
            }
        }
    }



    var filtered = filters ?
       $filter('filter')($scope.data, newFilters) :
       $scope.data;

    filtered = params.sorting() ?
                $filter('orderBy')(filtered, params.orderBy()) :
                filtered;

    params.total(filtered.length);

    $defer.resolve(filtered.slice((params.page() - 1) * params.count(), params.page() * params.count()));
}

We could probably think about some generic method extending filter object if there's a dot in the filter attribute (and do a PR ?). Would be a lot cleaner.

Florian F.
  • 4,700
  • 26
  • 50
2

You can create your own filter (ngTable v0.7.1):

filter="{client: 'clienttext'}"

...

<script type="text/ng-template" id="ng-table/filters/clienttext.html">
                <input type="text" name="filter-clienttext" ng-model="params.filter().client.name" class="input-filter form-control"/>
            </script>
vmartin
  • 493
  • 4
  • 15
2

As the previous accepted answer doesn't work anymore on nested params, here is my contribution for nested filtering / sortable / ordering getData()

$scope.tableParams = new NgTableParams({},
{
    getData: function($defer, params) {

        // organize filter as $filter understand it (graph object)
        var filters = {};
        angular.forEach(params.filter(), function(val,key){
            var filter = filters;
            var parts = key.split('.');
            for (var i=0;i<parts.length;i++){
                if (i!=parts.length-1) {
                    filter[parts[i]] = {};
                    filter = filter[parts[i]];
                }
                else {
                    filter[parts[i]] = val;
                }
            }
        })

        // filter with $filter (don't forget to inject it)
        var filteredDatas =
            params.filter() ?
                $filter('filter')(myDatas, filters) :
                myDatas;

        // ordering
        var sorting = params.sorting();
        var key = sorting ? Object.keys(sorting)[0] : null;
        var orderedDatas = sorting ? $filter('orderBy')(filteredDatas, key, sorting[key] == 'desc') : filteredDatas;

        // get for the wanted subset
        var splitedDatas =
            orderedDatas.slice((params.page() - 1) * params.count(),
                params.page() * params.count());

        params.total(splitedDatas.length);

        // resolve the ngTable promise
        $defer.resolve(splitedDatas);
    }
});
Nicolas Janel
  • 3,025
  • 1
  • 28
  • 31