-->

ngtable : sort and filter on nested object

2019-04-30 05:49发布

问题:

I have a list of objects to display on a table with ngTable. My object looks like :

obj {label:string,
     nestObj{nestLabel:string
            }
    }

In my controller I want to allow sorting and filtering on fields 'label' and 'nestObject.label'. I have tried this:

$scope.tableParams = new ngTableParams({
        page: 1,            // show first page
        count: 10,
        filter: {
            label='',
            nestObj.label=''
        },
        sorting: {
            label: 'asc',
            nestObj.label: 'asc'
        }
    }, {
        total: data.length, // length of data
        getData: function($defer, params) {
            // use build-in angular filter
            var filteredData = params.filter() ?
            $filter('filter')(data, params.filter()) :
            data;
            var orderedData = params.sorting() ?
                    $filter('orderBy')(filteredData, params.orderBy()) :
                    data;

            params.total(orderedData.length); // set total for recalc pagination
            $defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()));
    }          
});

But I m getting an error, the javascript compiler doesn't like the filter on nestObj.label :

Uncaugth syntexError : unexpected token .

IT works well if I don't filter and sort on nestObj.label.

Is it possible to filter and sort on nested object with ngTable?

Here is the plunker that illustrate the problem.

Thank you.

回答1:

Unfortunately, the filtering and sorting with nested objects is not suitable in ng-table for now. Reading this post and solution from @Kostia Mololkin, I finally got it how to avoid this bug and the solution is in the end very simple. Big thanks to him!

I just rewrote the array where is your data: initialized the new property and set the data from nested object into the new property like:

for (var i = 0; i < data.length; i++) {
  data[i].town = ""; //initialization of new property 
  data[i].town = data[i].adresse.town;  //set the data from nested obj into new property
}

You can see this solution here on plunker, now it's working like a charm...



回答2:

Just adding my two cents as I came across, don't be afraid to make use of functional programming. Flattening data is simple and in my opinion cleaner like so:

var flattenedArray = dataArray.map(function(obj) {
    return {
        label: obj.label,
        nestedLabel: dataItem.nestedObj.nestedLabel
    };
});

Then you can sort by label and nestedLabel in ngTable with ease.

Note: I realize we are making copies here so a little more memory is consumed.



回答3:

Here is a getData method to support nested params and additional support for ordering and paging. Need to inject $filter and rename myData with your own array of datas. Hope it can help.

$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);
    }
});