简体   繁体   中英

@Output with EventEmitter from child to parent, view doesn't update in parent

When a marker is clicked in map.component.ts, it emits the data to home.component.ts. This is detected by this line <x-map (change)="updateSelected($event)"></x-map>

So when updateSelected is run it updates opened to true .

When checking the console log it returns with true but the {{ opened }} in the toolbar and the sidenav still have opened as false .

When I resize the window slightly the view of home.component.ts updates and {{ opened }} in the toolbar shows true and the sidenav opens.

How can I overcome this issue?

home.component.html

<mat-toolbar color="primary" class="mat-elevation-z6">
  <mat-toolbar-row>
    <span fxLayoutAlign="start center">
      <button mat-icon-button (click)="toggleSidenav()">
        <mat-icon aria-label="Example icon-button with a heart icon">menu</mat-icon>
      </button>
      {{ opened }}
    </span>
  </mat-toolbar-row>
</mat-toolbar>

<mat-sidenav-container fullscreen class="sidenav-container">
  <mat-sidenav #sidenav mode="side" [opened]="opened" class="mat-elevation-z6">
    Sidenav content
  </mat-sidenav>
  <mat-sidenav-content>
    <x-map (change)="updateSelected($event)"></x-map>
  </mat-sidenav-content>
</mat-sidenav-container>

home.component.ts

import { Component, Input, OnInit, SimpleChanges } from '@angular/core';

    @Component({
      selector: 'x-home',
      templateUrl: './home.component.html',
      styleUrls: ['./home.component.scss']
    })
    export class HomeComponent implements OnInit {

      constructor() { }
      opened = false;

      updateSelected($event) {
        console.log($event);
        this.opened = true;
        console.log(this.opened);
      }   
    }

map.component.ts

import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
import { ApiService } from '../api.service';
import { } from '@types/googlemaps';

@Component({
  selector: 'x-map',
  templateUrl: './map.component.html',
  styleUrls: ['./map.component.scss']
})
export class MapComponent implements OnChanges, OnInit {
  constructor(private apiService: ApiService) { }
  map: any;
  markersArray: any[] = [];
  devices: any;    
  @Output() change: EventEmitter<any> = new EventEmitter();

  markerClick(marker) {
    google.maps.event.addListener(marker, 'click', () => {
      this.change.emit(this.devices.find(d => d.ChargeDeviceId === marker.title));
    });
  }

  plot() {
    for (let i = 0; i < this.devices.length; i++) {
      const marker = new google.maps.Marker({
        map: this.map,
        position: new google.maps.LatLng(this.devices[i].ChargeDeviceLocation.Latitude, this.devices[i].ChargeDeviceLocation.Longitude),
        title: this.devices[i].ChargeDeviceId,
      });
      this.markerClick(marker);
      this.markersArray.push(marker);
    }
  }

  ngOnChanges() {
    console.log(this.resize);
    if (this.resize) {
      this.onResize();
    }
  }

  ngOnInit() {
    this.apiService.get().subscribe(
      (res) => {
        this.devices = res['ChargeDevice'];
        this.plot();
      },
      (err) => {
        console.log(err);
      }
    );

    this.map = new google.maps.Map(document.getElementById('map'), {
      center: {lat: 54.797753, lng: -2.871329},
      zoom: 8
    });
  }
}

I was able to fix this by importing ApplicationRef and then adding this.app.tick(); to the end of my updateSelected() function.

import { ApplicationRef, Component, Input, OnInit, SimpleChanges } from '@angular/core';

@Component({
  selector: 'x-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {

  constructor(private app: ApplicationRef) { }
  opened = false;

  toggleSidenav() {
    this.opened = !this.opened;
  }

  setSidenav(val: boolean) {
    this.opened = val;
  }

  updateSelected($event) {
    console.log($event);
    this.opened = true;
    console.log(this.opened);
    this.app.tick();
  }

  ngOnInit() {
  }
}

The reason for your property not being updated is that change detection is not triggered as you expect it to. And the reason that change detection is not run is because the callback for your google maps method is run outside of the angular zone .

To force that code to be run within the angular zone, you can wrap it with NgZone like this:

import { ..., ..., NgZone } from '@angular/core';  // <= IMPORT

//...

export class MapComponent implements OnChanges, OnInit {
    constructor(private zone: NgZone, ...) { }   // <= INJECT

    // ....

    markerClick(marker) {
        google.maps.event.addListener(marker, 'click', () => {
            this.zone.run(() => {   // <= USE
                this.change.emit(this.devices.find(d => d.ChargeDeviceId === marker.title));
            });    
        });
    }
}

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