2

I use this directive. However, on the setAddress event output, no changes are detected in my component. The view is not updated. I d'ont understand.

For test, if i remove the google.maps.event.addListener to replace by a simple setTimeout to call invokeEvent. It works.

@Directive({
  selector: '[googleplace]',
  providers: [NgModel],
  host: {
    '(input)' : 'onInputChange()'
  }
})
export class GoogleplaceDirective  {
  @Output() setAddress: EventEmitter<any> = new EventEmitter();
  modelValue:any;
  autocomplete:any;
  private _el:HTMLElement;


  constructor(el: ElementRef,private model:NgModel) {
    this._el = el.nativeElement;
    this.modelValue = this.model;
    var input = this._el;
    this.autocomplete = new google.maps.places.Autocomplete(input, {});
    google.maps.event.addListener(this.autocomplete, 'place_changed', ()=> {
      var place = this.autocomplete.getPlace();
      this.invokeEvent(place);
    });
  }

  invokeEvent(place:Object) {
    this.setAddress.emit(place);
  }


  onInputChange() {
  }
}

In my component view

<input type="text" class="validation-address-input" style="margin-top: 100px;" [value]="form.customerAddress"
               (setAddress)="setCustomStartLocation($event)" googleplace>

In my component

/**
     *
     * @param place
     */
    setCustomStartLocation(place: Object) {
        this.currentStep = 2;
    }

2 Answers 2

6

Handler for place_changed is running outside angular zone. You need to run it like this:

constructor(..., private zone: NgZone) {
  ...
  google.maps.event.addListener(this.autocomplete, 'place_changed', ()=> {
    var place = this.autocomplete.getPlace();
    this.zone.run(() => this.invokeEvent(place)); // run inside angular zone
  });
Sign up to request clarification or add additional context in comments.

2 Comments

Excellent ! Thanks
since event emitter updates value on the parent component regardless of zone it runs in, the other approach could be to just inject app:ApplicationRef and call app.tick()
0

Another possible solution is to inject ApplicationRef and call tick() method to run change detection cycle:

export class GoogleplaceDirective  {

  constructor(private app: ApplicationRef, ...) {
    ...
  }

  invokeEvent(place:Object) {
    this.setAddress.emit(place);
    this.app.tick(); <-----------------------------------
  }

This is essentially what zone is doing:

_this._zone.onMicrotaskEmpty.subscribe({
    next: function () {
        _this._zone.run(function () {
            _this.tick();
        });
    }
});

9 Comments

If something will be initialized during update on the parent component then this something can go wrong during the next action. And you will have to run detectChanges or tick again
@yurzui, interesting, can you maybe create a plunker to show it?
That is why angular includes this.zone.run for subscribe
plnkr.co/edit/CgkColxdjdtnFFzhGXHm?p=preview Сhoose something then click on Change address to dd button. Then force update
See zone primer docs.google.com/document/d/… When async work gets scheduled, the callback function will execute in the same zone as the zone which existed at the time of invoking the async API. This allows the zone to be tracked across many async invocations.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.