Search code examples
angulartypescriptrxjsangular-routerangular9

Angular Route Change breaks Component Service Relationship for Observables


I have an Angular 9 App that was running happily with observable services of 'common' data. When I started to build in routing, rather than having all components on a single page, the service loads with the Component on first load.

However, on using a routerlink to navigate somewhere else and navigate back, all observables stop returning values and come back as undefined.

I have used an Angular CLI base routing project for this app, so all main components are set up in the standard way.

In the example code below users, and canEdit come back as undefined after navigating with routelink, but work correctly on first load.

User Service:

import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';
import { IUser } from '../interfaces/iuser';

@Injectable({
  providedIn: 'root'
})
export class UsersService {
  user$: Observable<IUser>;
  private userSubject = new Subject<IUser>();

  constructor() {
    this.user$ = this.userSubject.asObservable();

  }

  user(data) {
    console.log(data)
    this.userSubject.next(data);
  }
}

Component ts:

import { Component, OnInit } from '@angular/core';
import { UsersService } from 'src/app/services/users.service';
import { IUser } from 'src/app/interfaces/iuser';

@Component({
  selector: 'app-exchange-rate',
  templateUrl: './exchange-rate.component.html',
  styleUrls: ['./exchange-rate.component.scss']
})
export class ExchangeRateComponent implements OnInit {
  currentUser: IUser;

  title = 'Exchange Rates';

  constructor(
    private users: UsersService,
    ) {
    this.users.user$.subscribe((user) => {
      this.currentUser = user;
    });
  }

  ngOnInit(): void {
  }


  get canEdit() {
    if (this.currentUser.types.includes('admin')){
      return true;
    }
    else {
      return false;
    }
  }

}

Component html:

    <div>
        <ng-container *ngIf="canEdit; else fx1">
            Can Edit
        </ng-container>
        <ng-template #fx1>
            Cant Edit
        </ng-template>
    </div>

Sidebar Component with routerLink:

<a class="cell" routerLinkActive="active" [routerLink]="['exchange-rate']">Exchange Rate</a>
//... more routes... 

App-routing:

const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
    children: [{
      path: 'exchange-rate',
      component: ExchangeRateComponent
    },
    {
      path: 'other-pages',
      component: OtherPagesComponent
    },
    // ... etc

The error when returning to the component is

ERROR TypeError: Cannot read property 'types' of undefined
    at ExchangeRateComponent.get canEdit [as canEdit]

Solution

  • userSubject, and, by extension, user$, emits once, when userSubject.next(data) is called. So it doesn't exactly "stop returning values" as much as it doesn't have anything new to say.

    One fix would be to make userSubject a ReplaySubject (it will emit last value to new subscribers):

    userSubject: ReplaySubject<IUser> = new ReplaySubject(1);