Search code examples
angularsignalrsingleton

Destroy Angular service after destroy component


I have a problem with Angular SignalR service. My constructor in service call only once. Flow:

  1. Open browser and localHost for my app
  2. Login to User
  3. My header component call Service constructor normally and I have normal SignalR connection.
  4. I log out(without refresh browser - app still work on main login page)
  5. Header was destroyed and was called onDestroyed automatically(checked with console.log())
  6. I logIn to User again(without refresh page)
  7. constructor of SignalR Service didn't call again and I didn't have connection SignalR. Need to refresh page.

my Code below

@Injectable({
  providedIn: 'root'
})
export class SignalrService {
  private message$: Subject<string>;
  private messageGroup$: Subject<string>;
  private connection: signalR.HubConnection;
  connectionId: string;

  constructor() {
    console.log("constructor")
    this.message$ = new Subject<string>();
    this.messageGroup$ = new Subject<string>();
    this.connection = new signalR.HubConnectionBuilder()
      .withUrl(`${environment.apiUrl}/` + 'notify', {
        accessTokenFactory: () => localStorage.getItem('accessToken')
      })
      .withAutomaticReconnect([0, 5000, 10000])
      .build();

    this.disconnect();
    this.connect();
  }

  public connect(): void {
    this.connection.start()
      .then(() => this.addUserToGroup())
      .catch(err => console.log(err));
  }

  public addBroadcastToUser(): void {
    this.connection.on('BroadcastToUser', (message) => {
      this.message$.next(message);
    });
  }

  public addFeedsUser(): void {
    this.connection.on('AddFeedsUser', (message) => {
      this.messageGroup$.next(message);
    });
  }

  public getMessage(): Observable<string> {
    return this.message$.asObservable();
  }

  public getGroupTradeRoomMessage(): Observable<string> {
    return this.messageGroup$.asObservable();
  }

  public disconnect(): void {
    this.connection.stop();
  }

  public getConnectionId = () => {
    this.connection.invoke('getConnectionId').then(
      (data) => {
        this.connectionId = data;
      }
    );
  };

  public addUserToGroup = () => {
    this.connection.invoke('addUserToGroup').then(
      (data) => {
        this.connectionId = data;
      }
    );
  };
}

I call from my Header Components

export class HeaderComponent implements OnInit, OnDestroy {
  private signalRSubscription: Subscription;

  constructor(private signalrService: SignalrService) {

    this.signalrService.addBroadcastToUser();
    this.signalrService.addFeedsUser();

    this.signalRSubscription = this.signalrService.getMessage().subscribe(
      (message) => {
        this.onDataUpdate(message);
      });
  }
  
  ngOnDestroy(): void {
    console.log("destroy header")
    this.signalrService.disconnect();
    this.signalRSubscription.unsubscribe();
  }
}

I need build new Service when user logIn. I tried different ways. Init new Service in constructor of header app. It did't give normal result. How I can fix it with normal Angular solution.


Solution

  • Angular have you covered on this one!
    Instead of providing your service in root, add the SignalrService to your component's providers array. Then you will get a new SignalrService instance every time the component is created.

    @Component({
        ...
        providers: [SignalrService]
    })
    HeaderComponent {}
    

    Don't forget to remove the providedIn: 'root' from the SignalrService

    @Injectable()
    export class SignalrService {...}