I've been trying to set up a network plugin as a service on an app to tell whether there's internet or not.
In my example I've set up two components:
My problem is when trying to destroy the components as I navigate from one to the other. The component where the app is loaded never gets destroyed. Even if I implement something like @HostListener('window:beforeunload') on top of ngOnDestroy().
I have implemented ngOnDestroy on both components to either remove the listener or unsubscribe to the networkServices's observable respectively.
I noticed that if I refresh the app on say Home:
If I refresh the app on TEST the same behaviour happens but reversed
I've uploaded the project to a public git just in case: https://github.com/jjgl/ionicNetworkCapacitorTests/
but here are the main bits:
HOME
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Plugins, NetworkStatus, PluginListenerHandle } from '@capacitor/core';
const { Network } = Plugins;
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit, OnDestroy {
networkStatus: NetworkStatus;
networkListenerHome: PluginListenerHandle;
async ngOnInit() {
this.networkListenerHome = Network.addListener('networkStatusChange', (status) => {
console.log("Home Page Network status changed", status);
this.networkStatus = status;
});
this.networkStatus = await Network.getStatus();
}
ngOnDestroy() {
console.log('home destroyed')
this.networkListenerHome.remove();
}
}
TEST
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { ConnectionStatus, NetworkService } from 'src/app/services/network.service';
@Component({
selector: 'app-test-page',
templateUrl: './test-page.page.html',
styleUrls: ['./test-page.page.scss'],
})
export class TestPagePage implements OnInit, OnDestroy {
subscription : Subscription
private statusOnline : boolean;
constructor(private networkService: NetworkService) {}
async ngOnInit() {
console.log('hola')
this.subscription = this.networkService.onNetworkChange().subscribe((status: ConnectionStatus) => {
this.statusOnline = status == ConnectionStatus.Online ? true : false;
console.log('network change, status:', status);
})
}
ngOnDestroy() {
console.log('test destroyed')
this.subscription.unsubscribe()
}
}
Network Service
import { Injectable, OnDestroy } from '@angular/core';
import { Plugins, NetworkStatus, PluginListenerHandle } from '@capacitor/core';
import { BehaviorSubject, Observable } from 'rxjs';
const { Network } = Plugins;
export enum ConnectionStatus {
Online,
Offline
}
@Injectable({
providedIn: 'root'
})
export class NetworkService implements OnDestroy {
private netStatus: BehaviorSubject<ConnectionStatus> = new BehaviorSubject(ConnectionStatus.Offline);
networkStatus: NetworkStatus;
networkListener: PluginListenerHandle;
constructor() {
this.networkListener = Network.addListener('networkStatusChange', (status) => {
console.log("Service Network status changed", status);
this.networkStatus = status;
let auxStatus = status && status.connected ? ConnectionStatus.Online : ConnectionStatus.Offline;
this.netStatus.next(auxStatus)
});
this.initialState()
}
private async initialState(){
this.networkStatus = await Network.getStatus();
let auxStatus = this.networkStatus && this.networkStatus.connected ? ConnectionStatus.Online : ConnectionStatus.Offline;
this.netStatus.next(auxStatus)
}
public onNetworkChange(): Observable<ConnectionStatus> {
return this.netStatus.asObservable();
}
public getCurrentNetworkStatus(): ConnectionStatus {
return this.netStatus.getValue();
}
ngOnDestroy(): void {
console.log('services destroyed')
this.networkListener.remove();
}
}
Needless to say I'm new to Angular so feel free to point out other things too, all comments are welcome.
PS: I've implemented ngOnDestroy on the networkService, but I'm not calling it from TEST as the service doesn't get reinstantiated when navigating back to TEST after TEST.ngOnDestroy gets called the first time.
You cannot use Angular lifecycle in services. Use it inside components only.
Please call Network.addListener
into app.component.ts.
...
export class AppComponent implements OnInit {
subscription : Subscription
private statusOnline : boolean;
constructor(private networkService: NetworkService) {}
async ngOnInit() {
this.networkListener = Network.addListener('networkStatusChange', (status) => {
console.log("Service Network status changed", status);
this.networkStatus = status;
let auxStatus = status && status.connected ? ConnectionStatus.Online : ConnectionStatus.Offline;
this.networkService.netStatus.next(auxStatus) // Push the new status to NetworkService behaviorSubject
});
}
}
app.component.ts
is a root singleton component, so it's neither destroyed nor refreshed until you refresh the browser tab. Now you can use the netstatus in NetworkService inside any component like you are already doing.
...
export class NetworkService {
private netStatus: BehaviorSubject<ConnectionStatus> = new BehaviorSubject(ConnectionStatus.Offline);
public onNetworkChange(): Observable<ConnectionStatus> {
return this.netStatus.asObservable();
}
public getCurrentNetworkStatus(): ConnectionStatus {
return this.netStatus.getValue();
}
}