Ionic 4 - Network - Run in Angular Zone

Hi everyone,
recently I faced a problem with the Ionic Network library.
When I tried to change the UI on a network type change (connection or disconnection) the code is run but the UI does not change at all …
After some headaches, some strange logs ([…] chages outside Angular zone […]), and some Google searches … I found something.
It seems the Ionic Network library is run outside the Angular Zone (I’m quite noob with Angular, so at first I did not know what it meant …) even if this library is specified to work with Angular … (@ionic/network/ngx) …

So I think it is nice to share my method (maybe not the best, but it works ^^).

Step 1 : Creation of a pipe to run observable in the Angular zone
src/app/services/observable-util.service.ts

import {NgZone} from '@angular/core';
import {Observable, OperatorFunction} from 'rxjs';

export function runInZone<T>(zone: NgZone): OperatorFunction<T, T> {
  return (source) => {
    return new Observable(observer => {
      const onNext = (value: T) => zone.run(() => observer.next(value));
      const onError = (e: any) => zone.run(() => observer.error(e));
      const onComplete = () => zone.run(() => observer.complete());
      return source.subscribe(onNext, onError, onComplete);
    });
  };
}

Step 2 : Wrapping Ionic Network in a Service with «runInZone» pipe
src/app/services/network.service.ts

import {Injectable, NgZone} from '@angular/core';
import {Network} from '@ionic-native/network/ngx';
import {Observable} from 'rxjs';
import {runInZone} from './utils/observable-util.service';

/**
 * Network service in Angular zone
 */
@Injectable({
  providedIn: 'root'
})
export class NetworkService {

  constructor(private network: Network, private zone: NgZone) { }
  get Connection(): {
    UNKNOWN: string;
    ETHERNET: string;
    WIFI: string;
    CELL_2G: string;
    CELL_3G: string;
    CELL_4G: string;
    CELL: string;
    NONE: string;
  } {
    return this.network.Connection;
  }

  /**
   * Network type
   */
  get type(): string {
    return this.network.type;
  }

  /**
   * Returns an observable to watch connection changes
   */
  onChange(): Observable<any> {
    return this.network.onChange().pipe(runInZone(this.zone));
  }

  /**
   * Get notified when the device goes offline
   */
  onDisconnect(): Observable<any> {
    return this.network.onDisconnect().pipe(runInZone(this.zone));
  }

  /**
   * Get notified when the device goes online
   */
  onConnect(): Observable<any> {
    return this.network.onConnect().pipe(runInZone(this.zone));
  }
}

Step 3 : Use the new network service in place of Ionic Network
For example, trigger an ngrx redux store action

[…]
import {NetworkService} from './services/network.service';

@Component([…])
export class AppComponent implements OnInit {
    constructor(
        […]
        private store: Store<RootStoreState.State>,
        private network: NetworkService,
        […]
    ) {}
    […]
    ngOnInit() {
        this.network.onDisconnect().subscribe(() => this.store.dispatch(new ConnectionStatusStoreActions.Disconnect()));
        this.network.onConnect().subscribe(() => this.store.dispatch(new ConnectionStatusStoreActions.Connect({network: this.network.type})));
    }
    […]
}

Step 5 : The UI is refreshed on connection changes
You can chill and breath ^^

1 Like