简体   繁体   中英

How to filter json object by nested value in 2nd object with angular, without using ng-if or ng-show

Angular and Ionic newbie, working on an Ionic app and displaying assets received as json via an angular data service. I would like to filter my "asset" data via a second listing (of "groups") where each group object includes the IDs for those assets that are members of that group. This will be a second filter filtering the data presented in the view, and this group filter will present only those assets that are members of the selected group.

Here is the basic structure for my two objects (simplified):

"assets": [
    {"id": 1, "deviceName": "vehicle 1", "vRoadSpeed": 40},
    {"id": 2, "deviceName": "vehicle 2", "vRoadSpeed": 50},
    {"id": 3, "deviceName": "vehicle 3", "vRoadSpeed": 40}
]

and

"groups":[
        {"id": 1, "name": "downtown", "members": [{"id": 1},{"id": 2}]},
        {"id": 2, "name": "west", "members": [{"id": 1},{"id": 3}]}, 
        {"id": 3, "name": "east", "members": [{"id": 1}]}
]

I would like to filter values in the first object ("assets") by its id which corresponds to the the nested "members" id in the second object. eg: if I select group id 3 ("east"), the "assets" array would return only asset 1, "vehicle 1"

Here's the code I have so far:

controller:

.controller("AssetListCtrl",['$scope', 'dataService', function($scope, dataService){
    var assets = [];
    var groups = [];

    dataService.getAssets().then(function(assets){
        $scope.assets = assets;
    })

    dataService.getGroups().then(function(groups){
        $scope.groups = groups;
    }),

    $scope.filterFunction = function(element){
        return element.name.match(/^Ma/) ? true : false;
    };
}])

Directive:

.directive('testDirective',function(){
    return{
        controller: 'AssetListCtrl',
        replace: true,
        link: function(scope, element, attrs){

            scope.selectedGroups = {};
            scope.noGroupsSelected = true;

            scope.filterByGroup = function(){
                angular.forEach(scope.assets, function(asset){
                    var match = false;
                    angular.forEach(asset.groups, function(g) {
                        if (scope.selectedGroups[g.id]){
                            match = true;
                        }
                    });
                    asset.matchesGroup = match;
                });
                scope.noGroupsSelected = true
                angular.forEach(Object.keys(scope.selectedGroups), function(k) {
                    if (scope.selectedGroups[k]) {
                        scope.noGroupsSelected = false;
                    }
                });
            }
        }
    }
})

Data Service:

 .factory('dataService', function($http){
        var assets = {};
        var groups = {};
        //calling JSON array
        return {
            getAssets: function(){
                return $http.get('../lib/data/sampleData.json').then(function(response){
                    assets = response.data.assets; //populate variable 'assets'
                    return response.data.assets;
                });
            },
            getAsset: function(index){
                return assets[index];
            },
            getGroups: function(){
                return $http.get('../lib/data/sampleData.json').then(function(response){
                    groups = response.data.groups; //populate variable 'groups'
                    return response.data.groups;
                });
            }
        }
    })

html:

<div test-directive>
    Groups:
    <div ng-repeat = "group in groups">
        <label>
            <input type="checkbox" ng-model="selectedGroups[group.id]" name="groups_group" ng-change="filterByGroup()">{{group.name}} 
        </label>
    </div>
    <br>Assets
    <div class="content wrapper" infinite-scroll="addMoreItems()" infinite-scroll-distance="2">
        <ion-item ng-repeat="asset in filteredAssets = (assets | filter: query) | limitTo: config.itemsDisplayedInList track by asset.id" ng-if="asset.matchesGroup || noGroupsSelected" ui-sref='app.AssetListDetail({index: $index})'>
            <div class = "row">
                {{asset.deviceName}}
            </div>
        </ion-item>
    </div>
</div>

(Note I'm currently trying to implement the infinite scroll as recommended in this posting: http://www.williambrownstreet.net/blog/2013/07/angularjs-my-solution-to-the-ng-repeat-performance-problem/ . Per How to improve performance of ngRepeat over a huge dataset (angular.js)? , Ionic's 'collectionRepeat' directive is also supposed to be very performant, but it wouldn't work with 'ng-show' and 'ng-if' so have not included it here)

What I would like to do is similar to what is posed in this post: Angular JS - Creating a filter to compare 2 arrays . I have implemented the code as per the accepted answer, and it works, but I also need to implement a second filter on the data (essentially a general search field).

It looks like it's not good practice to use both a filter and ng-show ( Get the length of list shown by ng-Show 've substituted ng-if ). So how do I do that?

All suggestions and comments on data structure and code welcome.

Have had some clarifications on filtering objects based on nested value in 2nd JSON object and wanted to share.

Based on this JSON structure:

{
   "assets": [
        {
            "deviceName": "vehicle 25",
            "vpkDeviceID": 352964050744425,
            "driverName": "Mike Smith"
        },
        {
            "deviceName": "vehicle 52",
            "vpkDeviceID": 352599041910352,
            "driverName": "John Doe"
        },
        {
            "deviceName": "vehicle 11",
            "vpkDeviceID": 862170016156711,
            "driverName": "Sarah Johnson"
        },
        {
            "deviceName": "vehicle 28",
            "vpkDeviceID": 862193020453528,
            "driverName": "Eleanor Petit"
        }
    ],
    "groups":
        [
            {"ID": 1, "name": "nairobi", "members": [{"vpkDeviceID": 352964050744425}, {"vpkDeviceID": 352599041910352}, {"vpkDeviceID": 862170016156711}]}, 
            {"ID": 2, "name": "karen", "members": [{"vpkDeviceID": 352964050744425}, {"vpkDeviceID": 352599041910352}]}, 
            {"ID": 3, "name": "langata", "members": [{"vpkDeviceID": 352599041910352}, {"vpkDeviceID": 862170016156711}, {"vpkDeviceID": 862193020453528}]},
            {"ID": 4, "name": "downtown", "members": [{"vpkDeviceID": 352964050744425}, {"vpkDeviceID": 862170016156711}]}, 
            {"ID": 5, "name": "westlands", "members": [{"vpkDeviceID": 862193020453528}]}
        ]
}

filter structured as follows in html:

<form action="">
    Group:
    <select name="groups" id="grp" ng-model="selectedGroup" ng-options="group.name for group in groups">
        <option value="">Select All</option>
    </select>
</form>

<div>
    <div ng-repeat="asset in assets | filter: myFilter">
        {{asset.driverName}}
    </div>
</div>

and filter in controller:

$scope.myFilter = function(val, i, a) {
    if (!$scope.selectedGroup) {
        return true;
    };
    var m = false;
    var grp = $scope.selectedGroup;
    angular.forEach(grp.members, function(v, k) {
        if (val.vpkDeviceID == v.vpkDeviceID) {
            m = true;
        }
    }, m);
    return m;
};
$scope.selectedAsset = {};
$scope.selectedGroup = null;

Working plunk here . No ng-if or ng-show required.

Thanks to @jb-nizet for clarification on a related question that helped clean up how I was doing the filter. I had originally structured the filter to use ng-repeat , but he pointed out it was cleaner to do filter with stated criteria by using ng-options . Additionally, some code was unnecessary when restructured as such - please see post for details.

Note I had originally requested details on how to do filter as well as what most performant way to do filter was. Am removing aspect on performance, as that should have been a separate question.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM