简体   繁体   中英

Angular 2 manually triggering ngIf

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?

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;
      }
    });

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

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

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;
    }
)

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