Andrii Abramov
Andrii Abramov

Reputation: 10751

Angular 2: property becomes underfined

The problem is that the property which is passed to the view child becomes undefined:

Parent component:

@Component({
    selector: 'app-searchbox',
    template: `
            <app-artifact-list [artifacts]="searchResults"></app-artifact-list>
    `
})
export class SearchboxComponent implements OnInit {

    // explicitly initialize the property 
    // the same effect with constructor initialization
    searchResults: Artifact[] = [new Artifact()];

    constructor(private artifactService: ArtifactService) {
    }

    ngOnInit(): void {
        this.pollService(this.artifactService.findByQuery('guice'));
    }

    private pollService(request: Observable<Artifact[]>) {
        return request.subscribe(this.fillInResults);
    }

    private fillInResults(result: Artifact[]) {
        result.forEach(console.log);
        for (let obj of result) {
            // this.searchResults is undefined! why?
            this.searchResults.push(obj);
        }
    }

}

Child component:

@Component({
    selector: 'app-artifact-list',
    template: `
            <h1>{{_artifacts | json}}</h1>
    `
})
export class ArtifactListComponent implements OnChanges {

    private _artifacts: Artifact[] = [];

    ngOnChanges(changes: SimpleChanges): void {
        console.log('Property changed');
    }

    @Input()
    set artifacts(artifacts: Artifact[]) {
        console.error('Property changed');
        this._artifacts = artifacts;
    }

}

During constructor call I see that the property is properly initialized but in the callback method it becomes undefined.

Isn't it somehow related to this? Maybe this.searchResults refers to anything else in the callback?

Upvotes: 2

Views: 191

Answers (1)

Andrii Abramov
Andrii Abramov

Reputation: 10751

After some investigation into this when it is used in scope of "function references", I found out that actually this refers to another object. I love javascript :)

So the workaround is:

private pollService(request: Observable<Artifact[]>) {
    return request.subscribe(
        this.fillInResults.bind(this)
    );
}

private fillInResults(result: any) {
    result.forEach(console.log);
    for (let obj of result) {
        this.searchResults.push(obj);
    }
}

Or to use fat arrow function:

private pollService(request: Observable<Artifact[]>) {
    return request.subscribe(
        result => this.fillInResults(result)
    );
}

private fillInResults(result: any) {
    result.forEach(console.log);
    for (let obj of result) {
        this.searchResults.push(obj);
    }
}

See this question for detailed explanation: Angular2 component's “this” is undefined when executing callback function

Upvotes: 3

Related Questions