16

The following HTML, Javascript and JSON render correctly, but the filter does not work at all. What are we doing wrong?

<div data-ng-controller="dashboard_controller">
    <h1>
        Catalogs
        <input type="text" data-ng-model="catalog_filter" placeholder="Filter Distributors">
    </h1>

    <div class="catalogs_listing">
        <ul data-ng-repeat="catalog in catalogs | filter:catalog_filter">
            <li><a href="{{catalog.distributor_id}}/d/products/catalog_view/{{catalog.uid}}">
                <div class="catalog_thumb">
                    <div class="catalog_thumb_image">
                        <img src="{{catalog.thumb_image}}" />
                    </div>
                </div>
                <div class="catalog_info">
                    <h2>{{distributors[catalog.distributor_id].name}}
                        <span>{{catalog.products_count}}p</span>
                    </h2>
                    <p>{{catalog.name}}</p>
                </div>
                </a>
            </li>
        </ul>
    </div>
</div>

The Javascript:

app.controller('dashboard_controller', function ($scope, $http) {
    $http.get('./api/distributors/my').then(function (res) {
        $scope.distributors = res.data;
    });
    $http.get('./api/dashboard/catalogs').then(function (res) {
        $scope.catalogs = res.data;
    });
});

And these 2 JSONs:

api/distributors/my:

{
    "data": {
        "9kkE1sL8vXSZMVaL": {
            "created": "1346840145.22",
            "uid": "9kkE1sL8vXSZMVaL",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "Nikee",
            "description": "Just do it",
            "image_file": "LogoNike.jpg",
            "modified": "1368443518.3894",
            "modified_by": "3W7AoIQHTtvPauaK",
            "currency": "gbp"
        },
        "1OBKUhpb8srwHVVb": {
            "created": "1346840213.41",
            "uid": "1OBKUhpb8srwHVVb",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "Zappos",
            "description": "The webs most popular shoe store",
            "image_file": "zappos.jpg",
            "modified": "1347006513.93",
            "modified_by": "3W7AoIQHTtvPauaK",
            "currency": null
        },
        "qHPXDp5lSQuz9z3Q": {
            "created": "1346840305.78",
            "uid": "qHPXDp5lSQuz9z3Q",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "Kitchenaid",
            "description": "For the way it's made",
            "image_file": "kitchenaid_logo.gif",
            "modified": "1346840305.78",
            "modified_by": "3W7AoIQHTtvPauaK",
            "currency": null
        },
        "9K4G8gE1sh4qpVG2": {
            "created": "1346840443.32",
            "uid": "9K4G8gE1sh4qpVG2",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "Unilever",
            "description": "Create a better future",
            "image_file": "Unilever-logo.jpg",
            "modified": "1346842125.2",
            "modified_by": "3W7AoIQHTtvPauaK",
            "currency": null
        },
        "55ORaD7h0EMcaX82": {
            "created": "1346840529.93",
            "uid": "55ORaD7h0EMcaX82",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "Dell",
            "description": "The power to do more",
            "image_file": "dell-logo.jpg",
            "modified": "1346840529.93",
            "modified_by": "3W7AoIQHTtvPauaK",
            "currency": null
        },
        "2LHf5ZipYjA2PdXu": {
            "created": "1352084334.37",
            "uid": "2LHf5ZipYjA2PdXu",
            "created_by": "3MO4JyiB9rMWTfBu",
            "name": "Online Retailer",
            "description": "Online Retailer",
            "image_file": "Home and Giftware.gif",
            "modified": "1352954806.28",
            "modified_by": "cu3OraVD7WclpLrX",
            "currency": null
        },
        "MdTDL72ynFySuUCR": {
            "created": "1352870158.83",
            "uid": "MdTDL72ynFySuUCR",
            "created_by": "1JiAF71w5VPHGgJe",
            "name": "Uniuniform",
            "description": "Uniform Suppliers",
            "image_file": "CWLogo.png",
            "modified": "1358317144.85",
            "modified_by": "sv3HuiiRbiuHWkul",
            "currency": null
        },
        "oyYmdDcod9fseZng": {
            "created": "1352934703.42",
            "uid": "oyYmdDcod9fseZng",
            "created_by": "cu3OraVD7WclpLrX",
            "name": "Heidy Pharmaceuticals",
            "description": "Pharmaceutical Solutions",
            "image_file": "heidy.jpg",
            "modified": "1352934703.43",
            "modified_by": "cu3OraVD7WclpLrX",
            "currency": null
        },
        "Kfs4HdFUfz6j2l2I": {
            "created": "1352953682.22",
            "uid": "Kfs4HdFUfz6j2l2I",
            "created_by": "cu3OraVD7WclpLrX",
            "name": "xxx",
            "description": "Online Retailer",
            "image_file": "xxx.gif",
            "modified": "1352953828.34",
            "modified_by": "cu3OraVD7WclpLrX",
            "currency": null
        },
        "g2qRqUWvPSLRvLQr": {
            "created": "1352953924.68",
            "uid": "g2qRqUWvPSLRvLQr",
            "created_by": "cu3OraVD7WclpLrX",
            "name": "ddd",
            "description": "Natural Product Retailer",
            "image_file": "yes-to.jpg",
            "modified": "1352953924.68",
            "modified_by": "cu3OraVD7WclpLrX",
            "currency": null
        },
        "bbSu3jpFhdkG3TJR": {
            "created": "1352954016.22",
            "uid": "bbSu3jpFhdkG3TJR",
            "created_by": "cu3OraVD7WclpLrX",
            "name": "llll",
            "description": "Artificial Product Retailer",
            "image_file": "l.jpg",
            "modified": "1352954016.23",
            "modified_by": "cu3OraVD7WclpLrX",
            "currency": null
        },
        "X9xWF9VrRDqGWZ6S": {
            "created": "1352954722.97",
            "uid": "X9xWF9VrRDqGWZ6S",
            "created_by": "cu3OraVD7WclpLrX",
            "name": "zzz",
            "description": "Toy Manufacturer",
            "image_file": "zzz.jpg",
            "modified": "1352954722.97",
            "modified_by": "cu3OraVD7WclpLrX",
            "currency": null
        },
        "02CCPuWtM6ZJVgiQ": {
            "created": "1367741881.7113",
            "uid": "02CCPuWtM6ZJVgiQ",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "test brand",
            "description": "xxxx",
            "image_file": null,
            "modified": "1367741882.5129",
            "modified_by": "3W7AoIQHTtvPauaK",
            "currency": null
        },
        "GjsdgMCzp1n379j0": {
            "created": "1369136484.1802",
            "uid": "GjsdgMCzp1n379j0",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "testing all products",
            "description": "just a test",
            "image_file": null,
            "modified": "1369136484.5298",
            "modified_by": "3W7AoIQHTtvPauaK",
            "currency": "usd"
        },
        "spVsxtJVroMkXQ1N": {
            "created": "1370508658.353",
            "uid": "spVsxtJVroMkXQ1N",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "pppp Import",
            "description": "",
            "image_file": null,
            "modified": "1370508658.4394",
            "modified_by": "3W7AoIQHTtvPauaK",
            "currency": "usd"
        }
    }

api/dashboard/catalogs

{
    "data": {
        "UPf17vFhMhiM2yYl": {
            "created": "1352960014.4",
            "uid": "UPf17vFhMhiM2yYl",
            "created_by": "3MO4JyiB9rMWTfBu",
            "name": "All Products",
            "description": "This catalog contains all of your products",
            "modified": "1352960014.4",
            "modified_by": "3MO4JyiB9rMWTfBu",
            "distributor_id": "9kkE1sL8vXSZMVaL",
            "image": null,
            "start": null,
            "end": null,
            "is_archived": null,
            "products_count": "0",
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        },
        "ZUfcKpz0VrJZZZvW": {
            "created": "1354172792.79",
            "uid": "ZUfcKpz0VrJZZZvW",
            "created_by": "ORIGWlEFxbuE945J",
            "name": "test catalog",
            "description": "",
            "modified": "1354172792.79",
            "modified_by": "ORIGWlEFxbuE945J",
            "distributor_id": "9kkE1sL8vXSZMVaL",
            "image": null,
            "start": null,
            "end": null,
            "is_archived": null,
            "products_count": "0",
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        },
        "6YoSSDCzLH8gEokf": {
            "created": "1360706477.5283",
            "uid": "6YoSSDCzLH8gEokf",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "xxxx",
            "description": "",
            "modified": "1360706477.5312",
            "modified_by": "3W7AoIQHTtvPauaK",
            "distributor_id": "9kkE1sL8vXSZMVaL",
            "image": null,
            "start": null,
            "end": null,
            "is_archived": null,
            "products_count": "3",
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        },
        "4AwqE7iTNQmjSBED": {
            "created": "1360794567.8451",
            "uid": "4AwqE7iTNQmjSBED",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "All Products",
            "description": null,
            "modified": "1360794567.8454",
            "modified_by": "3W7AoIQHTtvPauaK",
            "distributor_id": "4AwqE7iTNQmjSBED",
            "image": null,
            "start": null,
            "end": null,
            "is_archived": null,
            "products_count": "1",
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        },
        "02CCPuWtM6ZJVgiQ": {
            "created": "1367741881.7916",
            "uid": "02CCPuWtM6ZJVgiQ",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "All Products",
            "description": null,
            "modified": "1367741881.7919",
            "modified_by": "3W7AoIQHTtvPauaK",
            "distributor_id": "02CCPuWtM6ZJVgiQ",
            "image": null,
            "start": null,
            "end": null,
            "is_archived": null,
            "products_count": "2095",
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        },
        "9kkE1sL8vXSZMVaL": {
            "created": "1368165852.0352",
            "uid": "9kkE1sL8vXSZMVaL",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "All Products",
            "description": null,
            "modified": "1368165852.0361",
            "modified_by": "3W7AoIQHTtvPauaK",
            "distributor_id": "9kkE1sL8vXSZMVaL",
            "image": null,
            "start": null,
            "end": null,
            "is_archived": null,
            "products_count": "26",
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        },
        "ZmSiqOK2C2Sq3MWB": {
            "created": "1368958571.9548",
            "uid": "ZmSiqOK2C2Sq3MWB",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "Test Catalog",
            "description": "",
            "modified": "1368958571.9581",
            "modified_by": "3W7AoIQHTtvPauaK",
            "distributor_id": "02CCPuWtM6ZJVgiQ",
            "image": null,
            "start": "0",
            "end": "0",
            "is_archived": "1",
            "products_count": "0",
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        },
        "flzoMbizDDTDpjgc": {
            "created": "1368958778.8623",
            "uid": "flzoMbizDDTDpjgc",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "xzczxc",
            "description": "",
            "modified": "1368958778.8637",
            "modified_by": "3W7AoIQHTtvPauaK",
            "distributor_id": "02CCPuWtM6ZJVgiQ",
            "image": null,
            "start": "0",
            "end": "0",
            "is_archived": "0",
            "products_count": "29",
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        },
        "KfRJHxp7jBnBGCJ5": {
            "created": "1369219487.4418",
            "uid": "KfRJHxp7jBnBGCJ5",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "hhh",
            "description": "",
            "modified": "1369219487.4433",
            "modified_by": "3W7AoIQHTtvPauaK",
            "distributor_id": "9kkE1sL8vXSZMVaL",
            "image": null,
            "start": "0",
            "end": "0",
            "is_archived": "0",
            "products_count": "7",
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        },
        "spVsxtJVroMkXQ1N": {
            "created": "1370508658.3567",
            "uid": "spVsxtJVroMkXQ1N",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "All Products",
            "description": null,
            "modified": "1370508658.3575",
            "modified_by": "3W7AoIQHTtvPauaK",
            "distributor_id": "spVsxtJVroMkXQ1N",
            "image": null,
            "start": "0",
            "end": "0",
            "is_archived": "0",
            "products_count": "343",
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        },
        "1OBKUhpb8srwHVVb": {
            "created": "1370857435.5606",
            "uid": "1OBKUhpb8srwHVVb",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "All Products",
            "description": null,
            "modified": "1370857435.5612",
            "modified_by": "3W7AoIQHTtvPauaK",
            "distributor_id": "1OBKUhpb8srwHVVb",
            "image": null,
            "start": "0",
            "end": "0",
            "is_archived": "0",
            "products_count": "4",
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        },
        "wXMHFdPyiXFBuRjJ": {
            "created": "1370864864.1247",
            "uid": "wXMHFdPyiXFBuRjJ",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "x",
            "description": "",
            "modified": "1370864864.1278",
            "modified_by": "3W7AoIQHTtvPauaK",
            "distributor_id": "spVsxtJVroMkXQ1N",
            "image": null,
            "start": "0",
            "end": "0",
            "is_archived": "0",
            "products_count": "10",
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        },
        "GjsdgMCzp1n379j0": {
            "created": "1371116610.6898",
            "uid": "GjsdgMCzp1n379j0",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "All Products",
            "description": null,
            "modified": "1371116610.6902",
            "modified_by": "3W7AoIQHTtvPauaK",
            "distributor_id": "GjsdgMCzp1n379j0",
            "image": null,
            "start": "0",
            "end": "0",
            "is_archived": "0",
            "products_count": "2095",
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        },
        "gvWLNWwsI3B7mnCU": {
            "created": "1371116669.5872",
            "uid": "gvWLNWwsI3B7mnCU",
            "created_by": "3W7AoIQHTtvPauaK",
            "name": "All Products",
            "description": null,
            "modified": "1371116669.5877",
            "modified_by": "3W7AoIQHTtvPauaK",
            "distributor_id": "gvWLNWwsI3B7mnCU",
            "image": null,
            "start": "0",
            "end": "0",
            "is_archived": "0",
            "products_count": 0,
            "thumb_image": "resources/media/default_image.jpg.thumbs/165x165.jpg"
        }
    }
5
  • 1
    The first thing i would try would be to do a $scope.$apply() inside the catalog callback after assignment done statement $scope.catalogs = res.data; Commented Jul 16, 2013 at 9:33
  • 2
    One thing that I see is that the ng-repeat data is not an array, but an object with named keys. It's possible that this confuses the filter. See also the comment in: stackoverflow.com/questions/16390151/… Commented Jul 16, 2013 at 10:06
  • which properties do you want to be used to filter? Your filer placeholder indicates that you would like to filter by distribtor name, right? Commented Jul 16, 2013 at 10:15
  • 1
    @Narretz - Please leave this comment as an answer, so I can accept it. Commented Jul 16, 2013 at 11:47
  • this answer in another thread saved my time : stackoverflow.com/a/14733280/4865619 Commented Aug 26, 2017 at 6:22

7 Answers 7

43

Angular filters cannot handle an object of objects as input. ng-repeat can render them, but filters expect an array of objects. The easiest way to fix this is to let the server return an array without named keys. You could also transform the response in angular after each request (more expensive).

Sign up to request clarification or add additional context in comments.

3 Comments

This answer saved my day, but why,... why can't filter handle named keys?
@jvannistelrooy it's simply not implemented, but there are patches pending. You can voice your support for this feature here: github.com/angular/angular.js/pull/6695 or here github.com/angular/angular.js/issues/6490 Filters are currently quite neglected by the angular devs (because they are working on different stuff) so the more people request this feature, the more likely it will be implemented eventually.
I don't think this answer is right. It is possible to do ng-repeat through an object of objects applying a filter at the same time. Look at this: stackoverflow.com/questions/41743724/…
34

As Narretz already mentioned, Angular filters cannot handle an object of objects as input. But you can write your own simple filter converting the object of objects into an array of objects. You can use the angular filters behind your array filter by chaining.

<ul data-ng-repeat="catalog in catalogs | array | filter:catalog_filter">

Your array filter could be as simple as this:

app.filter('array', function() {
  return function(items) {
    var filtered = [];
    angular.forEach(items, function(item) {
      filtered.push(item);
    });
   return filtered;
  };
});

1 Comment

When you read the documentation for the 10th time and somehow it's not clear that you can't use Objects even with $. THX
3

Assuming that you'd like to filter in catalogs using distributor name then the following custom filter does the job:

<div data-ng-controller="dashboard_controller">

<h1>
    Catalogs
    <input type="text" data-ng-model="catalog_filter"  placeholder="Filter Distributors">
</h1>

    <div class="catalogs_listing">
        <ul data-ng-repeat="catalog in catalogs | distributorName: catalog_filter:distributors">
            <li> <a href="{{catalog.distributor_id}}/d/products/catalog_view/{{catalog.uid}}">
                <div class="catalog_thumb">
                    <div class="catalog_thumb_image">
                        <span>demo - no image</span>
                    </div>
                </div>
                <div class="catalog_info">
                    <h2>{{distributors[catalog.distributor_id].name}} <span>{{catalog.products_count}}p</span></h2>
                    <p>{{catalog.name}}</p>
                </div>
            </a>

            </li>
        </ul>
    </div>
</div>

app.filter('distributorName', function() {
  return function(items, filterValue, distributors) {
      if (!filterValue){
          return items;
      }  
      var result = {};
        angular.forEach(items, function(value, key) {
            var distributor = distributors[value.distributor_id];
            if (distributor && distributor.name && distributor.name.toLowerCase().indexOf(filterValue.toLowerCase()) > -1){
                result[key] = value;
            }
        });
        return result;
    };
});

JSFiddle with local data: http://jsfiddle.net/alfrescian/p8zgp/

Comments

2

Very helpful to know ... here's a more generic version of alfrescian's solution, to filter a list of objects by a sub-key.

app.filter('objFilter', function() {
  return function(items, filter) {
      if (!filter){
          return items;
      }  
      var result = {};
        angular.forEach( filter, function(filterVal, filterKey) {
          angular.forEach(items, function(item, key) {
              var fieldVal = item[filterKey];
              if (fieldVal && fieldVal.toLowerCase().indexOf(filterVal.toLowerCase()) > -1){
                  result[key] = item;
              }
          });
        });
        return result;
    };
});

You can then have multiple filter boxes, like this

<input ng:model="filter.firstName"/>
<input ng:model="filter.lastName"/>
<input ng:model="filter.email"/>    

And your ng-repeat would look like this

<ul ng:repeat="cust in customers | objFilter: filter">
  <li>
    <a href="mailto:{{cust.email}}">{{cust.firstName}} {{cust.lastName}}</a>
  </li>
</ul>

Comments

1

You can use the toArrayFilter that converts the object to an array so it can be used by regular filters.

In your case it would be as:

<div ng-repeat="catalog in catalogs | toArray | filter:catalog_filter">
  {{ catalog.$key }} - {{ catalog.someProp }}
  <!-- code -->
</div>

Comments

1

What Is Worth Mentioning...

Given, this is rather a corner case, but I thought I'd share.

You've a data-structure; it needs to be a collection, but it requires very SOLID fundamentals and its own methods due to expected growth, complexity, or other constraints. So you decide to prototype this object as an array.

var OrdersDataModel = function OrdersDataModel(orders) {
    var thus = this;
    var orders = orders || [];

    function loadData(data) {
        this.splice.apply(this, [0, this.length].concat(data || orders));
        return this;
    }
    function serialize() {
        var serialized = JSON.stringify(this);
        return serialized;
    }
    function marshalData() {
        var marshalled = JSON.parse(this.serialize());
        return marshalled;
    }

    // export precepts
    this.load = loadData;
    this.serialize = serialize;
    this.marshalData = marshalData;

    return this;
};
OrdersDataModel.prototype = new Array();

This may suit your needs and perform well in your ng-repeat -- until comes the need to filter your "array":

<element ng-repeat="order in orders | filter:q" />

Your filter will fail (probably simply due to type-checks), and you'll wast a little time perhaps.

In my case, it can be easily remedied by modding my serialize method from

var serialized = JSON.stringify(this);

-- to --

var serialized = JSON.stringify(this.slice(0));

Though, a much better way to accomplish my goal would be to decorate an array using either the Decorator Pattern or Constructor-Hijacking:

Instead of:

OrdersDataModel.prototype = new Array();
...
this.orders = new OrdersDataModel();
this.orders.load(serverData);

-- use --

this.orders = OrdersDataModel.apply([]);
this.orders.load(serverData);

A lot of this may seem a little extraneous, but I expect this (and its encompassing module/controller) to expand quickly, so I've stuck as much to SOLID as possible to keep it air-tight.

Hope this saves you some time!

Comments

1

Angular filters cannot handle an object of objects as input, however, to resolve the issue you are facing, you can add a watch to the catalog_filter. As follows:

$scope.$watch('catalog_filter', function (value) {
        regex = new RegExp($scope.catalog_filter);
         if (!$scope.catalog_filter) return true;
         return regex.test(catalog.uid); /* can create multiple conditions based on the requirement*/
    });

Doing this the filter acts on the input text and filters when the text changes.

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.