4

I'm trying to filter an an array in the dom that uses ng-repeat. The filter works when I hard code the return, but when I try to use this, it gives me the error

Cannot read property minHeight of undefined.

So for some reason this is undefined, but it works in other functions in the controller. I'm not sure why it doesn't work in the filter.

I'm sure I'm missing something ridiculously simple, but I cannot find it

Controller

export class ItemComponent {

    /*@ngInject*/
    constructor($http) {
        this.$http = $http;
        this.minHeight = 0;
    }

   filter(row){
       console.log(this.minHeight);
       return row.minHeight > this.minHeight;
   }

HTML

ng-repeat="item in itemCtrl.items | filter: itemCtrl.filter"

Transpiled Code (from app.bundle.js in browser)

var ItemsComponent = exports.ItemsComponent = function () {
  /*@ngInject*/
  ItemsComponent.$inject = ['$http'];
  function ItemsComponent($http) {
    _classCallCheck(this, ItemsComponent);

    this.$http = $http;
    this.minHeight = 0;
  }

  _createClass(ItemsComponent, [{
    key: '$onInit',
    value: function $onInit() {
      this.loadItems();
    }
  }, {
    key: 'loaditems',
    value: function loadItems() {
      var _this = this;

      this.$http.get('/api/items').then(function (res) {
        _this.items = res.data;
        console.log(_this.items);
      }).catch(function (err) {
        console.log(err);
      });
    }
  }, {
    key: 'filter',
    value: function filter(row) {
      console.log(this.minHeight);

      return row.minHeight > 3;
    }
  }]);

  return ItemsComponent;
}();
Jeremy Wagner
  • 143
  • 1
  • 11
  • How does the transpiled code looks like? – lenilsondc Oct 19 '16 at 16:23
  • It looks exactly the same. Nothing is being minified. – Jeremy Wagner Oct 19 '16 at 16:36
  • Hmm, pure es6 so, could you please provide a fiddle reproducing the error, I tested by my self and it worked normally. – lenilsondc Oct 19 '16 at 16:45
  • I was actually wrong, adding the transpiled code now. You can see that this isn't in it – Jeremy Wagner Oct 19 '16 at 17:08
  • I've tested the transpiled code as well, and it does'n reproduce the error. What about the fiddle reproducing the error? – lenilsondc Oct 19 '16 at 17:29
  • I don't have a fiddle reproducing the error right now. How are you testing it? What could the differences be? – Jeremy Wagner Oct 19 '16 at 17:37
  • By just running your function manually with the arguments. But the problem can be other thing that we can't reproduce without the original scenario. – lenilsondc Oct 19 '16 at 17:39
  • try this plnkr: [Angular2 plunkr with http injectable - Mahesh](http://plnkr.co/edit/ADFdawFVTrxjJl1oMzO9?p=preview) This uses '@'injectable and '@'inject, in case you are not using a transcript compiler. Also refer [this](https://angular.io/docs/ts/latest/guide/server-communication.html) and [this](http://stackoverflow.com/questions/35370405/angular-2-http-cannot-resolve-all-parameters-for-appservice) – Mahesh Oct 19 '16 at 17:44
  • Thanks, not using Angular2 though. The big difference I see is that this appears in the transpiled functions that work: **var _this = this;** but it does not show up in the functions that do not. – Jeremy Wagner Oct 19 '16 at 18:10
  • It has to do with itemCtrl.filter in the dom. When I call the function in the $onIt in the controller, it finds this. – Jeremy Wagner Oct 19 '16 at 18:34
  • When I change the dom to use **itemCtrl.filter()**, the function finds this, but it doesn't pass the data from the ng-repeat array – Jeremy Wagner Oct 19 '16 at 18:50

2 Answers2

2

I'm pretty sure this is not the best approach, but since there is no answer and I could solve my problem this way, I will share it with you.

There is no this context in the filter function when called on ng-repeat (why?), so I solved it binding the context and getting the controller context from the this context.

HTML

ng-repeat="item in itemCtrl.items | filter: itemCtrl.filter.bind(this)"

Controller

filter(row) {
  console.log(this.itemCtrl.minHeight);
}
jpenna
  • 8,426
  • 5
  • 28
  • 36
0

Looks like you figured this out, but if you didn't want to bind in the template file, and I prefer to put as little as possible in the template file, you could bind in your class definition in the constructor.

For example:

export class ItemComponent {
    /*@ngInject*/
    constructor($http) {
        this.$http = $http;
        this.minHeight = 0;

        // arrow function will bind the 'this' you need, or you could use '.bind' instead
        this.filter = (row) => {
            console.log(this.minHeight);
            return row.minHeight > this.minHeight;
       }
    }
nicholascm
  • 621
  • 4
  • 7