简体   繁体   中英

Angular 12: ViewChild not working for ElementRef inside modals

I have modal on which I am searching location using google map services. I am getting error of TypeError: Cannot read properties of undefined (reading 'nativeElement')

HTML

<input
                type="text"
                [ngClass]="{ error_border: submitted && f.location.errors }"
                class="form-control"
                formControlName="location"
                [readonly]="viewMode"
                (keydown.enter)="$event.preventDefault()" 
                placeholder="Search Location" 
                autocorrect="off" 
                autocapitalize="off" 
                spellcheck="off" 
                #search
              />

TS

    export class ListComponent implements OnInit, AfterViewInit {
        
            dbLocation:any;
              latitude!: number;
              longitude!: number;
              zoom!: number;
              address!: string;
              private geoCoder:any;
              searchFlag:boolean = false;
              @ViewChild('search' , { static: true }) public searchElementRef!: ElementRef ;
            ngOnInit(): void {
            }
              ngAfterViewInit() {
//----------autocomplete code block------------
                 //load Places Autocomplete
                 this.mapsAPILoader.load().then(() => {
                  // this.setCurrentLocation();
                   this.geoCoder = new google.maps.Geocoder;
             
                   let autocomplete = new google.maps.places.Autocomplete(this.searchElementRef.nativeElement);
                   autocomplete.addListener("place_changed", () => {
                     this.ngZone.run(() => {
                       //get the place result
                       let place: google.maps.places.PlaceResult = autocomplete.getPlace();
             
                       //verify result
                       if (place.geometry === undefined || place.geometry === null) {
                         return;
                       }
             
                       //set latitude, longitude and zoom
                       this.latitude = place.geometry.location.lat();
                       this.longitude = place.geometry.location.lng();
                       this.getAddress(this.latitude, this.longitude);
                       this.zoom = 15;
                       this.searchFlag = true;
                     });
                   });
                 });
//----------autocomplete code block------------
                }
    }

I have tried adding { static: true } to viewChild. Also moving autocomplete code block from ngOnInit to ngAfterViewInit . Also adding timeout for autocomplete code block. But still giving that error.

It does not give error if input field is not on modal and autocomplete code block in ngOnInit . But I have form on modal in which I have that input field of location. There it is giving error.

I am not able to figure it out which combination of code will work. How can I resolve that error?

Please help and guide.

Edit modal HTML

<ng-template #formModal let-modal>
  <div class="modal-header">
    <h5 class="modal-title" id="modal-basic-title">
      Form
    </h5>
    <button
      type="button"
      class="close"
      aria-label="Close"
      (click)="modal.dismiss('Cross click')"
    >
      <span aria-hidden="true">
        <i class="fas fa-times"></i>
      </span>
    </button>
  </div>

  <div class="modal-body">
    <form [formGroup]="form">
      <div class="row edit_profile">
        <div class="col-sm-12">
          <div class="form-group">
            <label>Name<b style="color: red" *ngIf="!viewMode">*</b></label>
            <input
              type="text"
              [ngClass]="{ error_border: submitted && f.headline.errors }"
              formControlName="headline"
              class="form-control"
              [readonly]="viewMode"
            />
            <div *ngIf="submitted && f.headline.errors" class="text-danger">
              <div *ngIf="f.headline.errors.required">
                name is required
              </div>
            </div>
          </div>
        </div>
     

        <div class="col-sm-7">
          <div class="form-group">
            <label for="location">Location <b style="color: red" *ngIf="!viewMode">*</b></label>
            <div class="custome_input icons date_icon">
              <i class="fas fa-map-marker-alt"></i>
              <input
                type="text"
                [ngClass]="{ error_border: submitted && f.location.errors }"
                class="form-control"
                formControlName="location"
                [readonly]="viewMode"
                (keydown.enter)="$event.preventDefault()" 
                placeholder="Search Location" 
                autocorrect="off" 
                autocapitalize="off" 
                spellcheck="off" 
                #search
              />
            </div>
            <div *ngIf="submitted && f.location.errors" class="text-danger">
              <div *ngIf="f.location.errors.required">Location is required</div>
            </div>
          </div>
        </div>

      <div class="form-group btn-group mb-0" *ngIf="!viewMode">
        <button
          [disabled]="loading"
          class="btn"
          (click)="onSubmitForm()"
        >
          <span
            *ngIf="loading"
            class="spinner-border spinner-border-sm mr-1"
          ></span>
          Save
        </button>
      </div>
    </form>
  </div>
</ng-template>

TS of opening modal

 open(content) {
        
        this.listModalRef = this.modalService.open(content,{ size: 'lg', backdrop: 'static' });
        this.listModalRef.result.then((result) => {
          this.closeResult = `Closed with: ${result}`;
        }, (reason) => {
          this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
        });
        setTimeout(() => {
         //load Places Autocomplete
         this.mapsAPILoader.load().then(() => {
          // this.setCurrentLocation();
           this.geoCoder = new google.maps.Geocoder;
     
           let autocomplete = new google.maps.places.Autocomplete(this.searchElementRef.nativeElement);
           autocomplete.addListener("place_changed", () => {
             this.ngZone.run(() => {
               //get the place result
               let place: google.maps.places.PlaceResult = autocomplete.getPlace();
     
               //verify result
               if (place.geometry === undefined || place.geometry === null) {
                 return;
               }
     
               //set latitude, longitude and zoom
               this.latitude = place.geometry.location.lat();
               this.longitude = place.geometry.location.lng();
               this.getAddress(this.latitude, this.longitude);
               this.zoom = 15;
               this.searchFlag = true;
             });
           });
         });
        }, 0)
      }

First, you should remove { static: true } , else searchElementRef will always be undefined.

Next change you would need to do, is to move this.mapsAPILoader.load().then(() => {... } logic from ngAfterViewInit to a method wherein you are sure that the modal has been initialized. If you are using any library component (for modal) and if it provides an event (maybe onShown or show or whatever) to identify modal initialization, then you can put your logic there.

In short, you need to ensure that the modal is initialized before this.mapsAPILoader.load() promise is resolved, else you will always get searchElementRef as undefined.

put your code located inside your modal -> in a new component.

In this new component you can use @ViewChild to get an element value or native html element. Access the native element in ngAfterViewInit() (of your new component). It won't be undefined.

If you want to communicate with the parent component (component which holds the modal), use @Input/@Output, or use the ngrxStore (dispatch actions).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM