rgshenoy
rgshenoy

Reputation: 449

How to filter an nested object array by value in angular expression?

I'm using an ng-repeat where each object (item) is as below:

{
    "properties":
                [
                {"value":"started","key":"status"},
                {"value":"somename","key":"name"},
                {"value":"10","key":"age"},
                ]
}

How do I get the value corresponding to key 'status'

I have tried:

<span class="badge">{{item.properties['value'] | filter:{item:{properties:{key:'status'}}}}}</span>

but no luck.

Thanks, Rohith

Upvotes: 1

Views: 1462

Answers (1)

mhodges
mhodges

Reputation: 11116

What you want to filter is item.properties, not item.properties['value']. This will check each property and return an array of items that match your criteria. Since there will only be one match (I assume), you want to grab the resulting array[0] and then look at the .value. You do this by wrapping your filter in parentheses, and then accessing it just like a normal array of objects.

var app = angular.module("myApp", [])
.controller("myCtrl", function () {
  this.items =[{
    "properties":
    [
      {"value":"started","key":"status"},
      {"value":"somename","key":"name"},
      {"value":"10","key":"age"},
    ]
  },{
    "properties":
    [
      {"value":"finished","key":"status"},
      {"value":"somename","key":"name"},
      {"value":"15","key":"age"},
    ]
  },{
    "properties":
    [
      {"value":"error","key":"status"},
      {"value":"somename","key":"name"},
      {"value":"12","key":"age"},
    ]
  }];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp" ng-controller="myCtrl as Main">
  <div ng-repeat="item in Main.items">
    <div>{{ (item.properties | filter:{'key': 'status'})[0].value }}</div>
  </div>
</body>

Edit

To implement your own custom filter with nested property searching, you can do something like the following:

Not all of the conditions for partial matching are in there, but it should be enough to get a good idea of how it works.

var app = angular.module("myApp", [])
.filter("propertyFilter", function () {
  return function (items, property, value, strict) {
    return items.filter(function (item) {
      var propertyChain = property.split(".");
      var _propToCheck = item;
      // step through nested properties
      for(var prop of propertyChain) {
        if (_propToCheck.hasOwnProperty(prop)) {
          _propToCheck = _propToCheck[prop];
        }
        else {
          // return false if property does not exist
          return false;
        }
      }
      if (strict) {
        // exact matches only
        return _propToCheck === value;
      }
      else {
        // partial matching -- incomplete, but enough to show an example
        if (typeof _propToCheck === "string"){
          return _propToCheck.toLowerCase().indexOf(value.toLowerCase()) > -1;
        }
        else if (_propToCheck instanceof Array) {
          return _propTocheck.some(function (elem) {
            return _propToCheck.toLowerCase().indexOf(value.toLowerCase()) > -1;
          })
        }
        else {
          return _propToCheck == value;
        }
      }
    });
  }
})
.controller("myCtrl", function () {
  this.items =[{
    "properties":
    [
      {"value":"started","key":{"internalKey": {"_internalInternalKey": 'status'}}},
      {"value":"somename","key":"name"},
      {"value":"10","key":"age"},
    ]
  },{
    "properties":
    [
      {"value":"finished","key":{"internalKey": "status"}},
      {"value":"somename","key":"name"},
      {"value":"15","key":"age"},
    ]
  },{
    "properties":
    [
      {"value":"error","key":"status"},
      {"value":"somename","key":"name"},
      {"value":"12","key":"age"},
    ]
  }];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp" ng-controller="myCtrl as Main">
  <div ng-repeat="item in Main.items" style="margin: 10px;">
    <div>key: {{ (item.properties | propertyFilter:'key':'status')[0].value }}</div>
    <div>key.internalKey: {{ (item.properties | propertyFilter:'key.internalKey':'status')[0].value }}</div>
    <div>key.internalKey._internalInternalKey: {{ (item.properties | propertyFilter:'key.internalKey._internalInternalKey':'status')[0].value }}</div>
  </div>
</body>

Upvotes: 1

Related Questions