Search code examples
javascriptangulartypescriptgoogle-maps-api-3angular-ng-if

Why will a google maps API component work without *NgIf but works if excluded? Angular 9


I tried following the steps laid out in this thread. It works to load searchboxes with this component:

map.component.html

<input id= 'box2'  *ngIf="boxReady" class="controls" type="text" placeholder="Search Box">
<input id = 'box' class="controls" type="text" placeholder="Search Box">

map.component.ts

import {Component, Input, OnInit} from '@angular/core';
import {GoogleAPIService} from '../google-api.service';
declare var google: any;
@Component({
  selector: 'app-map',
  templateUrl: './map.component.html',
  styleUrls: ['./map.component.scss'],
})
export class MapComponent implements OnInit {
  @Input() boxIdVar: string;
  // @Input() id: string;
  boxReady = false;

  constructor(private _google: GoogleAPIService){}

  ngOnInit(): void {
    if (typeof google !== 'undefined') {
      console.log(google);
      console.log('Map.NgInit');
      console.log(this.boxIdVar);
      this.boxReady = true;
      let input = document.getElementById('box');
      let originSearch = new google.maps.places.SearchBox(input);
      input = document.getElementById('box2');
      let dest = new google.maps.places.SearchBox(input);
    }
    else {
      console.log('Google was undefined Map');
    }
  }
}

However, if I add an *NgIf statement that will load one of the boxes only when the component is loaded then that searchbox no longer will work and I get an uncaught promise exception.

map.component.html

<input id= 'box2'  *ngIf="boxReady" class="controls" type="text" placeholder="Search Box">
<input id = 'box' class="controls" type="text" placeholder="Search Box">

error found in the console:

ERROR Error: "Uncaught (in promise): TypeError: b is null
v$.prototype.o@https://maps.googleapis.com/maps-api-v3/api/js/40/5/places_impl.js:78:66

This all arose because I wanted to dynamically change the Id of the input box. But it seams that whenever an HTML element has some angular bound variable the functionality breaks.


Solution

  • You are querying the DOM in ngOnInit(). This is too early in the component lifecycle for Angular to have added it to the DOM. Setting the *ngIf condition to true doesn't immediately add it to the DOM.

    Instead, you should run any code that requires the DOM element later in the component lifecycle, in ngAfterViewInit().

    Also, a more "Angular" way to get a DOM element is to use @ViewChild().

    <input *ngIf="boxReady" #box2 />
    
    @ViewChild('box2') box2: ElementRef;
    
    boxReady = false;
    
    ngOnInit(): void {    
      this.boxReady = true;    
    
      const viewChild = this.box2 ? this.box2.nativeElement : null;
      console.log('ngOnInit viewChild', viewChild); // null
    }
    
    ngAfterViewInit() {  
      const viewChild = this.box2.nativeElement;
      console.log('ngAfterViewInit viewChild', viewChild);
    }
    

    DEMO: https://stackblitz.com/edit/angular-2um7rp