2

The solution to this issue might be fairly simple. This is what happens when a DBA tries to code ;)

I am using Angular 5 here is the package.json:

"@angular/animations": "^5.2.8",
"@angular/cdk": "^5.2.4",
"@angular/common": "^5.2.8",
"@angular/compiler": "^5.2.8",
"@angular/core": "^5.2.8",
"@angular/forms": "^5.2.8",
"@angular/http": "^5.2.8",
"@angular/material": "^5.2.4",
"@angular/platform-browser": "^5.2.8",
"@angular/platform-browser-dynamic": "^5.2.8",
"@angular/router": "^5.2.8",
"@ngrx/effects": "^4.1.1",
"@ngrx/router-store": "^4.1.1",
"@ngrx/store": "^4.1.1",
"@ngrx/store-devtools": "^4.1.1",
"@types/googlemaps": "^3.30.7",
"core-js": "^2.4.1",
"lodash.clonedeep": "^4.5.0",
"rxjs": "^5.5.6",
"zone.js": "^0.8.20"

I am trying to call a service inside a function. The function works pretty well and is triggered as expected. when I do a console.log, I find all my data inside the function as expected. Things are getting annoying when I call a service inside the function. The service itself as a value equal to "null"... So there I am lost :)

Here is the html:

<form class="autocomplete-form">
  <mat-form-field class="autocomplete-full-width">
    <input matInput placeholder="ICAO code" aria-label="Airport"  [matAutocomplete]="auto" [formControl]="airportCtrl">
    <mat-autocomplete #auto="matAutocomplete" [displayWith]="loadAirport">
    <mat-option *ngFor="let airport of airports" [value]="airport">
    <span>{{ airport.icao_code }}</span> <small>{{ airport.name }} ( {{ airport.country_name }} )</small>
    </mat-option>
  </mat-autocomplete>
  </mat-form-field>
</form>

Here is the detail of the component (Yes the service is provided inside the app.module) :

The part declared in the constructor works as expected (including the service).

import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { ActivatedRoute, Router } from "@angular/router";
import { Airport } from '../models/airport.model';
import { AutocompleteService } from '../../../../services/autocomplete.service'
import 'rxjs/add/operator/debounceTime';
import { Store, State } from "@ngrx/store";
import { Observable } from 'rxjs/Observable';
import { AirportsState } from '../models/airports-state.model';
import { 
  PolpulateSelectedAirportAction
  , POPULATE_SELECTED_AIRPORT
  , LoadingSelectedAirportData
} from '../store/airports.actions';


@Component({
  selector: 'app-autocomplete-airport',
  templateUrl: './autocomplete-airport.component.html',
  styleUrls: ['./autocomplete-airport.component.scss']
})


export class AutocompleteAirportComponent implements OnInit  {

airportCtrl = new FormControl();

 airports: any;
 scope: string;
service: AutocompleteService;
airport: Airport;

 constructor(private autocompleteService: AutocompleteService, private store: Store<AirportsState>,private router: Router, private route: ActivatedRoute){
 let options;
 this.scope = 'airport';
 this.service = autocompleteService

 this.airportCtrl.valueChanges
    .debounceTime(700) 
    .subscribe(data => {
      if (data.length > 3) {
        this.service.loadField(data, this.scope).subscribe(response =>{

            options = JSON.stringify(response);

            console.log('searchResult:' + options);
            this.airports = new Airport;
            console.log(this.airports);
            return this.airports = response;

        })
      }
    })




 }

 ngOnInit() {}

  loadAirport(airport): string | undefined {
   let apt;

   if (airport) {
    console.log('Airport ID inside loadAirport : ' + airport.id);

     this.service.displayMap(airport).subscribe(response => {
       apt = JSON.stringify(response);
      console.log('searchResult:' + apt);
  })

}


return airport ? airport.icao_code+' - '+airport.name : undefined;

}


} 

So when the function loadairport is called, the console throws an error:

ERROR TypeError: Cannot read property 'displayMap' of undefined at MatAutocomplete.AutocompleteAirportComponent.loadAirport [as displayWith]

If I test the service with if (this.service == null) it says that it is null.

Could someone help me?

Thanks a million in advance.

Pierre H.
  • 107
  • 2
  • 12

1 Answers1

3

This is a specific case of this problem.

loadAirport is passed as a callback to component input. Once it is passed, original this is lost and no longer equal to component instance it originally belonged to.

loadAirport should be bound to the context either by defining it as arrow method:

loadAirport = (airport) => {...}

Or by binding it to component instance in constructor:

constructor(...) {
  this.loadAirport = this.loadAirport.bind(this);
  ...
}

As explained in this answer, the latter is preferable for several reasons.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565