How to handle connection loss page in Angular 7?
Asked Answered
C

2

11

What I want is that, If the network is not available, and the user tries to navigate to the next page ConnectionLost Component would be there.

But if there is no network and user do not take any action means not navigating to the second page. then there should not be a connection-lost page. The user should stay on the current page.

For that, I have implemented canActivate guard as the following code:

@Injectable({
  providedIn: 'root'
})
export class NetworkGuard implements CanActivate {
  constructor(private router: Router, private network: NetworkService) {
  }

  canActivate(next: ActivatedRouteSnapshot,
              state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    if (window.navigator.onLine) {
      return true;
    } else {
      if (!this.isConnectionLostComponent()) {
        this.router.navigate(['/connection-lost'], {skipLocationChange: true});
      }
      return false;
    }
  }

  private isConnectionLostComponent() {
    // check whether connection lost component is added on not
    return this.network.isActive;
  }
}

It's working fine except for one condition. That is if I click back or forward from the browser, its update URL connection-lost to the address bar

How could I solve this problem? Can see the sample Here

steps to produce the issue:

  1. Click banner(Button) -> URL change to '/banner'
  2. Click brand(Button) -> URL change to '/brand'
  3. Disconnect network on that brand page
  4. Click back from browser-> ConnectionLostComponent and url is '/brand', that's okay
  5. Click back again -> ConnectionLostComponent but url is also changed to '/connection-lost'. that's what I'm facing the problem.

I just don't want to update the URL with '/connection-lost', for that I added skipLocationChange: true option to router.navigate method in the NetworkGuard, But still it's not working.

Competitive answered 18/2, 2019 at 15:8 Comment(3)
You could store the last visited URL, and if the users goes back to it, then prevent your condition ? You can't do much else, as the browsers forbid you to know if the user clicked on the back button.Candicecandid
developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLineDinh
@trichetriche I can check last visited URL but how could I prevent user to go back until he/she is online, and I have added a similar type of condition in isConnectionLostComponent() method, please check my updated question.Competitive
C
6

After a little bit of search, and with help of @AlokeT's answer. I got a solution to this problem.

@AlokeT's suggestion is showing connection loss page as soon as the user lost his/her network. but my requirement is to show that connection loss page while he/she tries to navigate to another page.

And In this answer, I just added, that missing part.

For that, I just update that isNetworkStopped flag from Guard, and because every CanActivate guard executes before navigation starts. So connection lost component would show while the user changing the path.

There is a code of NetworkService which I'm using in NetworkGuard,

@Injectable({providedIn: 'root'})
export class NetworkService {

  online: boolean;
  isNetworkStopped = false;

  constructor() {
    this.online = window.navigator.onLine;

    fromEvent(window, 'online').subscribe(e => {
      this.online = true;
    });

    fromEvent(window, 'offline').subscribe(e => {
      this.online = false;
    });
  }
}

Here in above code I just added a flag isNetworkStopped. And updated that flag from NetworkGuard, means while the user tries to navigate to next page and found no network.

And also removed navigation from NetworkGuard. See my below, updated code of NetoworkGuard

@Injectable({providedIn: 'root'})
export class NetworkGuard implements CanActivate {
  constructor(private network: NetworkService) {
  }

  canActivate(next: ActivatedRouteSnapshot,
              state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    if (this.network.online) {
      return true;
    }
    this.network.isNetworkStopped = true;
    return false;
  }
}

And based on that flag I managed to show ConnectionLost component. For that ConnectionLostcomponent is added on condition based to root component's template.

app.component.html

<router-outlet *ngIf="!networkService.isNetworkStopped"></router-outlet>
<app-connection *ngIf="networkService.isNetworkStopped"></app-connection>

And from ConnectionLost component, if the user clicks on the reload button. By checking network connection I updated isNetworkStopped flag of NetworkService.

Competitive answered 23/2, 2019 at 18:50 Comment(2)
Since the networkService already has a "online" property, why it still need the isNetworkStopped flag?Tophole
Yes, @Tophole it's true, but as user navigating to another component then I'm changing this(isNetworkStopped) flag. so that user can see connection lost component while he/she changing the route, not as soon as connection lost. That's my primary requirement.Competitive
H
10

I don't know is it the right solution or not for you but what I did in my project is following.

app.component.ts

constructor(){
this.onlineOffline = Observable.merge(of(navigator.onLine),
      fromEvent(window, 'online').pipe(map(() => true)),
      fromEvent(window, 'offline').pipe(map(() => false))
    );
}

app.component.html

<ng-container *ngIf="onlineOffline | async; then thenTemplate; else elseTemplate"></ng-container>
<ng-template #thenTemplate>
 <router-outlet></router-outlet>
</ng-template>
<ng-template #elseTemplate>
  <app-no-network></app-no-network>
</ng-template>

Let me know if it is working the way you need to implement it or not.

Hamper answered 19/2, 2019 at 5:43 Comment(7)
this is a good answer and I tried it before, but this time I want to show 'app-no-network' component on navigation. not as fast as the user disconnected from the network.Competitive
BTW what the benefit of doing that? I just want to know. @RaviSevtaHamper
A user can see current page content if there is no network. taking action is a secondary thing. @ AlokeTCompetitive
So you can make a transparent overlay and activate it when there is no network so user only can see content, but unable to do anything. also add <app-no-network> a property called show which will show a small changes to the page like YouTube does on its android app.Hamper
but I have to show a connection break UI on no internet.Competitive
Yes, thats what I want.Competitive
Let us continue this discussion in chat.Hamper
C
6

After a little bit of search, and with help of @AlokeT's answer. I got a solution to this problem.

@AlokeT's suggestion is showing connection loss page as soon as the user lost his/her network. but my requirement is to show that connection loss page while he/she tries to navigate to another page.

And In this answer, I just added, that missing part.

For that, I just update that isNetworkStopped flag from Guard, and because every CanActivate guard executes before navigation starts. So connection lost component would show while the user changing the path.

There is a code of NetworkService which I'm using in NetworkGuard,

@Injectable({providedIn: 'root'})
export class NetworkService {

  online: boolean;
  isNetworkStopped = false;

  constructor() {
    this.online = window.navigator.onLine;

    fromEvent(window, 'online').subscribe(e => {
      this.online = true;
    });

    fromEvent(window, 'offline').subscribe(e => {
      this.online = false;
    });
  }
}

Here in above code I just added a flag isNetworkStopped. And updated that flag from NetworkGuard, means while the user tries to navigate to next page and found no network.

And also removed navigation from NetworkGuard. See my below, updated code of NetoworkGuard

@Injectable({providedIn: 'root'})
export class NetworkGuard implements CanActivate {
  constructor(private network: NetworkService) {
  }

  canActivate(next: ActivatedRouteSnapshot,
              state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    if (this.network.online) {
      return true;
    }
    this.network.isNetworkStopped = true;
    return false;
  }
}

And based on that flag I managed to show ConnectionLost component. For that ConnectionLostcomponent is added on condition based to root component's template.

app.component.html

<router-outlet *ngIf="!networkService.isNetworkStopped"></router-outlet>
<app-connection *ngIf="networkService.isNetworkStopped"></app-connection>

And from ConnectionLost component, if the user clicks on the reload button. By checking network connection I updated isNetworkStopped flag of NetworkService.

Competitive answered 23/2, 2019 at 18:50 Comment(2)
Since the networkService already has a "online" property, why it still need the isNetworkStopped flag?Tophole
Yes, @Tophole it's true, but as user navigating to another component then I'm changing this(isNetworkStopped) flag. so that user can see connection lost component while he/she changing the route, not as soon as connection lost. That's my primary requirement.Competitive

© 2022 - 2024 — McMap. All rights reserved.