Home > OS >  Better way to initialize all the global services when user Logged out
Better way to initialize all the global services when user Logged out

Time:09-30

I need to clear all data or initialize all the global services when the user Logged out. So I have used window.location.reload(); and it does the job. But can I have a better approach here since my current hack gives bad UI/UX to the user?

  private refreshTheApp(): void {
   
      window.location.reload();
    
  }

The problem is with this kind of global Singleton service:

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

CodePudding user response:

Simplest Approach

The simplest approach would involve having something like an AppEventsService with a few subjects exported as observables so that any component/service could subscribe to its changes.

import { Injectable } from '@angular/core';

import { Observable, Subject } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class AppEventsService {
  public userSignOut$: Observable<void>;
  private userSignOut$$ = new Subject<void>();

  constructor() {
    this.userSignOut$ = this.userSignOut$$.asObservable();
  }

  public emitUserSignOutEvent(): void {
    this.userSignOut$$.next();
  }
}

That way when the user signs out you can call this.appEventsService. emitUserSignOutEvent() and any other service/component can subscribe to this event like this.appEventsService.userSignOut$.subscribe(() => {...})

More elaborated approach

A better approach could be to have something like an app events system where each event has a type and a typed payload. Please take a look at this Github repo.

IMPORTANT: This is just a demo and there's definitely a lot of room for improvements!!!

The basic elements from this demo are the events, a custom filter operator and the AppEventsService.

1) Events

Defining an event is a bit verbose but the benefit is that the payload of the event will be typed.

For example, the following code defines the UserSignInEvent:

import { BaseEvent } from './base.event';

export const USER_SIGN_IN = 'USER_SIGN_IN';

// Event definition
export interface UserSignInEvent {
  type: typeof USER_SIGN_IN;
  payload: {
    id: number;
    email: string;
  };
}

// Event creator function
export const userSignInEvent = (
  id: number,
  email: string
): UserSignInEvent => ({
  type: USER_SIGN_IN,
  payload: { id, email },
});

// Event filtering function
export const isUserSignInEvent = (event: BaseEvent): event is UserSignInEvent =>
  event.type === USER_SIGN_IN;

The code above not only defines the event, but also defines a function that allows us to create an instance of the event, and another function that allows us to recognise the event based on its type. They will be used later when dispatching and subscribing to the event.

2) Filter operator

This operator allows us (obviously) to filter the events based on its type so that each subscriber could listen to specific events. But the operator also makes its payload to be typed:

import { OperatorFunction } from 'rxjs';
import { filter, map } from 'rxjs/operators';

export const filterEvent =
  <T, R extends T>(guard: (value: T) => value is R): OperatorFunction<T, R> =>
  (source) =>
    source.pipe(
      map((value) => {
        if (guard(value)) {
          return value;
        }

        return null;
      }),
      filter((value) => !!value)
    );

3) The service

This is a very simple service exposes a method to dispatch events and another method to subscribe to specific events:

import { Injectable } from '@angular/core';

import { Observable, Subject } from 'rxjs';

import { BaseEvent } from '../events/base.event';

@Injectable({ providedIn: 'root' })
export class AppEventsService {
  private eventsDispatcher = new Subject<BaseEvent>();

  public dispatch(event: BaseEvent): void {
    this.eventsDispatcher.next(event);
  }

  public get onEvents$(): Observable<BaseEvent> {
    return this.eventsDispatcher.asObservable();
  }
}

Using the events

Dispatching an event is super simple thanks to the function that help us to create instances of the events:

import { Component } from '@angular/core';
import { userSignInEvent, userSignOutEvent } from 'src/app/events';
import { AppEventsService } from 'src/app/services/app-events.service';

@Component({
  selector: 'app-tab2',
  templateUrl: 'tab2.page.html',
  styleUrls: ['tab2.page.scss'],
})
export class Tab2Page {
  constructor(private appEventsService: AppEventsService) {}

  public dispatchSignInEvent(): void {
    const id = Math.floor(Math.random() * 10);
    const email = `test ${id}@user.com`;

    console.log(`===> TAB 2: about to dispatch UserSingInEvent: ${id} - ${email}`);
    this.appEventsService.dispatch(userSignInEvent(id, email));
  }

  public dispatchSignOutEvent(): void {
    console.log(`===> TAB 2: about to dispatch UserSingOutEvent`);
    this.appEventsService.dispatch(userSignOutEvent());
  }
}

And then the last part of the code is to subscribe to these events.

import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { isUserSignInEvent, isUserSignOutEvent } from 'src/app/events';
import { filterEvent } from 'src/app/operators';
import { AppEventsService } from 'src/app/services/app-events.service';

@Component({
  selector: 'app-tab1',
  templateUrl: 'tab1.page.html',
  styleUrls: ['tab1.page.scss'],
})
export class Tab1Page implements OnInit, OnDestroy {
  public id: number;
  public email: string;
  public isSignedIn: boolean;

  private unsubscribe$ = new Subject<void>();

  constructor(private appEventsService: AppEventsService) {}

  ngOnInit() {
    this.appEventsService.onEvents$
      .pipe(filterEvent(isUserSignInEvent), takeUntil(this.unsubscribe$))
      .subscribe(({ payload }) => {
        const { id, email } = payload;

        console.log(`===> TAB 1: received UserSingInEvent: ${id} - ${email}`);

        this.id = id;
        this.email = email;
        this.isSignedIn = true;
      });

    this.appEventsService.onEvents$
      .pipe(filterEvent(isUserSignOutEvent), takeUntil(this.unsubscribe$))
      .subscribe(() => {
        console.log(`===> TAB 1: received UserSingOutEvent`);

        this.id = null;
        this.email = null;
        this.isSignedIn = false;
      });
  }

  ngOnDestroy() {
    this.unsubscribe$.next();
    this.unsubscribe$.unsubscribe();
  }
}

The most important part of that code is this one:

  this.appEventsService.onEvents$
      .pipe(filterEvent(isUserSignInEvent)) // <----
      .subscribe(({ payload }) => {
        // the payload is typed here!
      });

Thanks to the filterEvent operator and the isUserSignInEvent function the event's payload is typed :)

  • Related