3

I'm using Angular 2 and GeoFire to display nearby professionals in an area. I created a service to use GeoFire to return an observable of a list of professionals, but I'm getting a massive delay between getting that list and having it displayed.

This is my method to get the professionals in my service:

public getKeysFromGeoQuery(): Observable<any> {
    var keys = new Array();

    return Observable.create(observer => {
        this.geoQuery.on("key_entered", (key, location, distance) =>  {
            keys.push(key);                
            observer.next(keys);
        });

        this.geoQuery.on("key_exited", (key, location, distance) => {
            var index = keys.indexOf(key);
            if (index > -1) {
                keys.splice(index, 1);
            }              
            observer.next(keys);
        });
    });
}

This is my test component to use the service:

import { Component } from '@angular/core';
import { LocationService } from '../../core/location.service';
import { Observable, Observer } from 'rxjs';

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.css']
})
export class TestComponent {
  mapCenter = [34.021256, -118.403630];
  searchRadius = 4;

  result;
  error;
  time;

  constructor(private locationService: LocationService) {
    const startTime = Date.now();
    this.locationService.getGeoQuery({ center: this.mapCenter, radius: this.searchRadius });

    this.locationService.getKeysFromGeoQuery()
      .subscribe(keys => {
        this.result = keys;
        console.log("keys: ", this.result);
        this.time = Date.now() - startTime;
        console.log("time: ", this.time);
      });
  }
}

test.component.html:

<p>Result: {{result}} </p>
<p>Time: {{time}} </p>
<p>Error: {{error}} </p>

In my console, it shows the keys getting retrieved within 500ms. However, the browser does not show the result until 3-4 seconds later. Does anyone know what is causing this delay? Screen and console

RedFour
  • 43
  • 5

1 Answers1

2

It sounds like the GeoFire on event is being fired outside of a zone.

You could verify this by injecting a ChangeDetectorRef and explicitly triggering change detection:

import { ChangeDetectorRef } from '@angular/core';
...
@Component({
  ...
})
export class TestComponent {
  ...
  constructor(
    private locationService: LocationService,
    private changeDetectorRef: ChangeDetectorRef
  ) {
    ...
    this.locationService.getKeysFromGeoQuery()
      .subscribe(keys => {
        this.result = keys;
        console.log("keys: ", this.result);
        this.time = Date.now() - startTime;
        console.log("time: ", this.time);
        this.changeDetectorRef.detectChanges();
      });
  }
}

If it is the case that the event is being fired outside a zone, it could be related to how the Firebase and GeoFire scripts are incorporated into your build. Perhaps they are being loaded before zone.js?

cartant
  • 57,105
  • 17
  • 163
  • 197
  • Thank you! That fixed my issue perfectly. Looks like I need to read up on zone.js – RedFour Jan 06 '17 at 06:31
  • It seems like the solution is to add `this.changeDetectorRef.detectChanges();` to all my `subscribe` and `then` clauses. When you say they are loaded before zone.js, is there a way to change that? Do I need to change my package.json so the dependencies are in a different order? – RedFour Jan 06 '17 at 06:40
  • You'd need to provide all of the details re: your build process. If I were in your situation, I would do some searching re: the tools you are using and Firebase and zone.js, etc. – cartant Jan 06 '17 at 07:02
  • Once you get the script orders sorted out, you should be able to get it working, as [this person](http://stackoverflow.com/q/41455969/6680611) had the opposite problem. Having to sprinkle `ChangeDetectorRef`s all over the place would be tedious. – cartant Jan 06 '17 at 07:11
  • Thank you! I'm seeing the same issue using Ionic 4. I thought smart/fast change detection was one of the perks of Angular, but until I unpack what it is zone.js does, adding `changeDetectorRef.detectChanges()` does the trick. – briznad Sep 05 '18 at 20:09