7

I have a code that looks like this:

export class CRListComponent extends ListComponent<CR> implements OnInit {

    constructor(
        private router: Router,
        private crService: CRService) {
        super();
    }

    ngOnInit():any {
        this.getCount(new Object(), this.crService.getCount);
    }

The ListComponent code is this

@Component({})
export abstract class ListComponent<T extends Listable> {

    protected getCount(event: any, countFunction: Function){
        let filters = this.parseFilters(event.filters);
        countFunction(filters)
            .subscribe(
                count => {
                    this.totalItems = count;
                },
                error => console.log(error)
            );
    }

And the appropriate service code fragment from CRService is this:

getCount(filters) {
    var queryParams = JSON.stringify(
        {
            c : 'true',
            q : filters
        }
    );

    return this.createQuery(queryParams)
        .map(res => res.json())
        .catch(this.handleError);
}

Now when my ngOnInit() runs, I get an error:

angular2.dev.js:23925 EXCEPTION: TypeError: Cannot read property 'createQuery' of undefined in [null]

ORIGINAL EXCEPTION: TypeError: Cannot read property 'createQuery' of undefined

So basically, the this in the return this.createQuery(queryParams) statement will be null. Does anybody have an idea how is this possible?

3
  • 1
    Why do you think this.createQuery exists? Commented Apr 27, 2016 at 8:16
  • 2
    is it "normal" that your 2 classes are missing a closing bracket ? Commented Apr 27, 2016 at 8:16
  • I just omitted the unnecessary parts, the code is syntactically correct otherwise. :) The problem is not with .createQuery, it's with this itself. Commented Apr 27, 2016 at 8:34

2 Answers 2

10

The problem is located here:

gOnInit():any {
    this.getCount(new Object(), this.crService.getCount); // <----
}

Since you reference a function outside an object. You could use the bind method on it:

this.getCount(new Object(), this.crService.getCount.bind(this.crService));

or wrap it into an arrow function:

this.getCount(new Object(), (filters) => {
  return this.crService.getCount(filters));
});

The second approach would be the preferred one since it allows to keep types. See this page for more details:

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

3 Comments

Thanks, it seems to work! Is there a way I can do this in HTML template bindings? I mean, I have the following code: (onLazyLoad)="loadItems($event, this.crService.getCRs)" Can I also transform this into something like this? (onLazyLoad)="loadItems($event, (page, n, sortField, sortOrder, filters) => { return this.crService.getCRs(page, n, sortField, sortOrder, filters)})"
Great! You don't need to use the this keyword in templates. That said, I would use an intermediate level. I mean: using the this.crService.getCRs within the loadItems method...
Good, I also ended up with this solution. This seems also much cleaner than embedding such ugly code in my HTML templates. Thanks for the help! :)
2

To fix this error I yanked all the innards out of my function causing the error and threw it in another function then the error went away.

example:

I had this function with some code in it

this.globalListenFunc = renderer.listenGlobal('document', 'click', (event) => {
  // bunch of code to evaluate click event
  // this is the code that had the "this" undefined error
});

I pulled the code out and put it in an external public function, here's the finished code:

this.globalListenFunc = renderer.listenGlobal('document', 'click', (event) => {
  this.evaluateClick(event);
});

evaluateClick(evt: MouseEvent){
    // all the code I yanked out from above
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.