0

Currently, my DOM object looks like this:

<div *ngIf="checkCam1Exists()">
  <app-counter [index]="1"></app-counter>
</div>

Within the component, the function 'checkCam1Exists()' looks like this:

checkCam1Exists(){
  console.log("checkCam1ran");
  if(this.cameraService.camera1 == null){
    return false;
  }
  else{
    return true;
  }
}

Where cameraService is a service which is injected into the components constructor, and camera1 is an object within cameraService.

My issue lies with getting ngIf to run again. The camera1 object is updated from within another service (a dataService which makes a HTTP request). How do I trigger ngIf to run again?

I've read the post below: Triggering Angular2 change detection manually - but the examples provided within the Angular docs don't seem to cover how to check variables outside of the component class (and I need to check service variables).

EDIT

I managed to get automatic change detection working using:

constructor(private ref: ChangeDetectorRef, private cameraService: CameraService, private toolbarService: ToolbarService) {
    ref.detach();
    setInterval(() => {
      this.ref.detectChanges();
    }, 5000);
  }

Although this feels like an incredibly inefficient way to do this. How can I manually trigger the change detection?

EDIT 2

With help from Lucas Tétreault, I think I might be going outside of the Angular 2 zone for the initial click event, as any interaction with the application (after the initial click event) causes ngIf to detect the change and start working again. My issue must lie here:

map.data.addListener('click', (event) => {
      var mapElement = event.feature.getProperty('type');
      switch(mapElement){
        case 'classifiedroads':
          var roadnumber = event.feature.getProperty('roadID');
          map.data.revertStyle();
          map.data.overrideStyle(event.feature, {
            strokeWeight: 8
          });
          this.roadService.roadClicked(roadID);
          break;
        case 'cameras':
          var cameraID = event.feature.getProperty('trafficID');
          this.cameraService.cameraClicked(cameraID); //camera is initialised at this stage.
          break;
        default:
          break;
      }
    });

I have a Google Map object imported using JavaScript (there are limitations with the Angular2 Google Map project that I was warned against which is why I am not using that). This click event must go outside the Angular 2 zone and when I click somewhere else on the application, I re-enter the Angular 2 zone and the ngIf change detection starts up again.

Is there a way I can manually force the ngIf change detection?

Community
  • 1
  • 1
dandev91
  • 1,691
  • 3
  • 22
  • 34

3 Answers3

1

This is what worked for me in the end.

I assume this to be correct from the research I have done: Using Google Maps with AddListener meant creating JavaScript listeners outside of the Angular 2 zone. This means that if variables or something changes, Angular 2’s change detection will not be fired. To combat this, inject NgZone within the constructor of the class:

constructor(private cameraService: CameraService, private ngZone: NgZone)...

Use NgZone's '.run()' to re-enter the Angular 2 zone:

map.data.addListener('click', (event) => {
      var mapElement = event.feature.getProperty('type');
      switch(mapElement){
        case 'cameras':
          var cameraID = event.feature.getProperty('traffic_asset_bk');
          this.ngZone.run(
              () => this.cameraService.cameraClicked(station_key)
          );
          break;
        default:
          break;
      }
    });
dandev91
  • 1,691
  • 3
  • 22
  • 34
0

Instead of trigerring manually you could do this right in the html

ng-if="cameraService.camera1 != null"

Lucas Tétreault
  • 953
  • 5
  • 15
  • I would love to +1 your answer since I did not realise I could reference injected services directly from the HTML (so thank you for that advice!), but unfortunately, I still have the same issue where ngIf still needs to be manually triggered once the cameraService object changes. – dandev91 Oct 13 '16 at 04:13
  • Change detection for the component will pick up the changes to the service: http://plnkr.co/edit/lxgAJs – Lucas Tétreault Oct 13 '16 at 05:16
  • So using your advice, I looked into it further. I am going outside of the Angular 2 zone for the initial click (A Google Map object imported using JavaScript). When I click on a static object in the Google Map, JavaScript handles the click event (due to Google Maps) and even though the variables are updated, ngIf change detection never happens. The moment I interact with the website anywhere else (literally by clicking anywhere else), the view updates and the camera appears. I assume I have to look into ngZone for the map click event then. – dandev91 Oct 13 '16 at 05:31
  • Lucas Tétreault, I added a second edit to the question to better explain my scenario. I have a JavaScript 'listener' which unfortunately I cannot remove, which is what begins the chain of functions which initialise the camera (and change its variable to something other than null). – dandev91 Oct 13 '16 at 05:51
0

I know I am late to the party but there are a couple ways in the Angular world to implement something like this. Personally for this case I would prefer using a BehaviorSubject observable. Essentially within your camera service you would instantiate a BehaviorSubject observable, and then within your component you would subscribe to it, and save the output upon each update to a property within your component, which would be what you run *ngIf against. I have done this in several projects and it works very well.

camera.service.ts

private cameraStatus: BehaviorSubject<ModalProperties> = new BehaviorSubject<boolean>(false);
    cameraStatus$ = this.cameraStatus.asObservable();

updateCameraStatus(status: boolean) {
    this.cameraStatus.next(status);
}

component-where-you-update-camera-status.ts

turnCameraOn() {
    this._CameraService.updateCameraStatus(true);
}

your-component.component.ts

componentVisibile: boolean

this_CameraService.cameraStatus$.subscribe(
    _CameraStatus => {
        this.componentVisible = _CameraStatus;
    }
)
joshrathke
  • 7,564
  • 7
  • 23
  • 38