11

I want to filter object inside nested ng-repeat.

HTML:

<div ng-controller="MyController">
<input type="text" ng-model="selectedCityId" />
<ul>
    <li ng-repeat="shop in shops">
      <p ng-repeat = "locations in shop.locations | filter:search" style="display: block">
          City id: {{ locations.city_id }}
          <span style="padding-left: 20px; display: block;" ng-repeat="detail in locations.details | filter:item">Pin code: {{detail.pin}}</span>
      </p>    
    </li>
</ul>

Controller:

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

myApp.controller('MyController', function ($scope) {

    $scope.search = function (location) {

        if ($scope.selectedCityId === undefined || $scope.selectedCityId.length === 0) {
            return true;
        }

           if (location.city_id === parseInt($scope.selectedCityId)) {
               return true;
            }
    };

    $scope.item = function (detail) {

        if ($scope.selectedCityId === undefined || $scope.selectedCityId.length === 0) {
            return true;
        }
        if (detail.pin == parseInt($scope.selectedCityId)) {
            return true;
        }
    };

    $scope.shops =
    [
       {
          "category_id":2,
          "locations":[
             {
                "city_id":368,
                "details": [{
                    "pin": 627718,
                  "state": 'MH'
                }]
             }
          ]
       },
       {
          "name":"xxx",
          "category_id":1,
          "locations":[
             {
                "city_id":400,
                "region_id":4,
                "details": [{
                    "pin": 627009,
                  "state": 'MH'
                },{
                    "pin": 129818,
                    "state": 'QA'
                }]
             },
          ]
       },
    ];

});

Here's the fiddle:

http://jsfiddle.net/suCWn/210/

I want to use multiple filter inside ng-repeat.

Example: Whenever user enters the ID in the input box. The list should filter based on cityID or PinCode. if user enter '129818' it should show pin code result of 129818 along with its parent cityID Similarly, if a user enter 400, the list should filter and show cityID result with 400 along with its child pin code.

EDIT:

Update Code http://codepen.io/chiragshah_mb/pen/aZorMe?editors=1010]

4 Answers 4

6

First, you must not filter locations with matching details. Use something like this in the search filter:

$scope.search = function (location) {
    var id = parseInt($scope.selectedCityId);
    return isNaN(id) || location.city_id === id || 
           location.details.some(function(d) { return d.pin === id });
};

To show details if filtered by cityID, you have to find the parent location and check if it was filtered.

$scope.item = function (detail) {
    var id = parseInt($scope.selectedCityId);
    return isNaN(id) || detail.pin === id || locationMatches(detail, id);
};

function locationMatches(detail, id) {
    var location = locationByDetail(detail);
    return location && location.city_id === id;
}

function locationByDetail(detail) {
    var shops = $scope.shops;
    for(var iS = 0, eS = shops.length; iS != eS; iS++) {
      for(var iL = 0, eL = shops[iS].locations.length; iL != eL; iL++) {
        if (shops[iS].locations[iL].details.indexOf(detail) >= 0) {
          return shops[iS].locations[iL];
        }
      }
    }
}

EDIT Another, more flexible solution would be to remove all the filters from ngRepeats and do the filtering in a method that you call on ngChange of the search text. Here is the basic structure for this approach.

myApp.controller('MyController', function($scope, $http) { 
  var defaultMenu = [];
  $scope.currentMenu = [];
  $scope.searchText = '';

  $http.get(/*...*/).then(function (menu) { defaultMenu = menu; } );

  $scope.onSearch = function() {
    if (!$scope.searchText) {
      $scope.currentMenu = defaultMenu  ;
    }
    else {
      // do your special filter logic here...
    }
  };
});

And the template:

<input type="text" ng-model="searchText" ng-change="onSearch()" />
<ul>
    <li ng-repeat="category in currentMenu">
      ...   
    </li>
</ul>
Sign up to request clarification or add additional context in comments.

12 Comments

Filtering through city id should also show it's pin code.
@chiragshah Should it hide the other children, if searched by pin code?
Yes. It should hide other children
@chiragshah A little bit ugly, but should work. (for some reason, i cannot update the fiddle anymore...)
Seems working well. The only problem is $scope.shops is updated ($http response object) once after initial page load which disables the item filter for some reason. Can you provide a fiddle for the same ?
|
2
+25

I have updated your filters. The problem is in your search filter you are only checking for the city_id, what you should do is:

  1. Check if the typed id is city_id
  2. Check if typed id is a pid of a child detail of given location

Similar thing for the item filter:

  1. Check if the typed id is a pid of the detail being filtered
  2. Check if the typed id is a city_id of the parent location of the detail passed in

Here is a working jsFiddle. I hope this helps.

Comments

1

By simply modifying the JSON to include the city_id for children so you don't need to loop through it to get the parent's city_id, the solution is as easy as this:

var myApp = angular.module('myApp', []);
myApp.controller('MyController', function ($scope) {
    $scope.search = function (location) {
        if (!$scope.selectedCityId)
            return true;
        //if user's input is contained within a city's id
        if (location.city_id.toString().indexOf($scope.selectedCityId) > -1)
            return true;
        for (var i = 0; i < location.details.length; i++)
            //if user's input is contained within a city's pin
            if (location.details[i].pin.toString().indexOf($scope.selectedCityId) > -1)
                return true;
    };

    $scope.item = function (detail) {
        if (!$scope.selectedCityId)
            return true;
        //if user's input is contained within a city's id
        if (detail.city_id.toString().indexOf($scope.selectedCityId) > -1)
            return true;
        //if user's input is contained within a city's pin
        if (detail.pin.toString().indexOf($scope.selectedCityId) > -1)
            return true;
    };

Modified JSON

$scope.shops=[{"category_id":2,"locations":[{"city_id":368,"details":[{"city_id":368,"pin":627718,"state":'MH'}]}]},{"name":"xxx","category_id":1,"locations":[{"city_id":400,"region_id":4,"details":[{"city_id":400,"pin":627009,"state":'MH'},{"city_id":400,"pin":129818,"state":'QA'}]},]},];});

If directly modifying the JSON is not possible, you can modify it like this in this controller directly after this $scope.shops = ...json... statement:

for(var i=0; i<$scope.shops.length; i++)
    for(var j=0, cat=$scope.shops[i]; j<cat.locations.length; j++)
        for(var k=0, loc=cat.locations[j]; k<loc.details.length; k++)
            loc.details[k].city_id=loc.city_id;

Working fiddle: http://jsfiddle.net/87e314a0/

1 Comment

codepen.io/chiragshah_mb/pen/aZorMe?editors=1010] This is the actual use case. Can you provide solution for this ?
1

I tried to make the solution easier to understand :

index.html :

<div ng-controller="MyController">
    <input type="text" ng-model="search.city_id" />
    <ul>
        <li ng-repeat="shop in shops">
          <p ng-repeat = "locations in shop.locations | filter:search.city_id" style="display: block">
              City id: {{ locations.city_id }}
              <span style="padding-left: 20px; display: block;" ng-repeat="detail in locations.details | filter:item">Pin code: {{detail.pin}}</span>
          </p>    
        </li>
    </ul>
</div>

app.js :

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

myApp.controller('MyController', function ($scope) {
    $scope.shops =
[
   {
      "category_id":2,
      "locations":[
         {
            "city_id":368,
            "details": [{
                "pin": 627718,
              "state": 'MH'
            }]
         }
      ]
   },
   {
      "name":"xxx",
      "category_id":1,
      "locations":[
         {
            "city_id":400,
            "region_id":4,
            "details": [{
                "pin": 627009,
              "state": 'MH'
            },{
                "pin": 129818,
                            "state": 'QA'
            }]
         },
      ]
   },
];


});

Here's the fiddle : mySolution

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.