Search code examples
angularangular10

Angular Subscription gets undefined after re-initializing component


I have one component with a value selection option value selection. I am subscribing to its value on initialization in every other component. From the first initialization, the value is returned, but after I change the component and return back the value is undefined. The service is created to set the value to the observable.

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

  private chooseLocations = new Subject<string[]>();

  chosenLocation$ = this.chooseLocations.asObservable();

  chosenLocations(locations: string[]): void {
    this.chooseLocations.next(locations);
  }
}

I use this serive in the selection component. Here is the selector Component

<mat-select (selectionChange)="onSelection($event)" [(value)]="selected">
  <mat-option [value]="allLocationsOption">All Locations</mat-option>
  <mat-option *ngFor="let location of locations.data" [value]="[location.id.toString()]">
<!-- The value is either one item or multiple -->
        {{location.name}}
  </mat-option>
</mat-select>

And How I set value using the LocationFilterService

export class LocationFilterComponent implements OnInit {
  ...
  constructor(private locationFilterService: LocationFilterService) {
  }

  ngOnInit(): void {
    this.locationFilterService.chosenLocations(['1']); //default value as an example
  }

 onSelection(event: MatSelectChange) {
    this.selectedLocations = [];
    this.selectedLocations = event.value;
    this.locationFilterService.chosenLocations(this.selectedLocations);
  }
}

It works well when I subscribe to its value for the first time in any other component in the tree

export class ExampleComponent implements OnInit, OnDestroy {
     subscription: Subscription;
     chosenLocations: string[];

     constructor(
     private locationFilterService: LocationFilterService) {}

     ngOnInit(): void {
       this.subscription = this.locationFilterService.chosenLocation$.subscribe(
       (loc_id) => {
           this.chosenLocations = loc_id;
           this.doSomeStuff();
           //Do some API call with chosenLocations values. First time it works, but once I 
           //leave this component and return back then this.chosenLocations is undefined
  }
);

    ngOnDestroy(): void {
    this.subscription.unsubscribe(); 
    }
}

Eventhough I set the default value in this example as ['1'] it gets undefined after component is destroyed and initialized again. The LocationFilterComponent is always present above other components that rely on this value. The goal is to maintain this selection values in other components that are rendered in under this LocationFilterComponent. Each of this new components rendered are subscribing and unsubscribing from the LocationFilterService. What would be a solution to this issue and what am I missing in this mechanism? Angular Version 10


Solution

  • The issue here is that you are using Subject. Instead you should used BehaviorSubject.

    A BehaviorSubject holds one value. When it is subscribed it emits the value immediately. A Subject doesn't hold a value.

    Subject example (with RxJS 5 API):

    const subject = new Rx.Subject();
    subject.next(1);
    subject.subscribe(x => console.log(x));
    
    Console output will be empty
    

    BehaviorSubject example:

    const subject = new Rx.BehaviorSubject(0);
    subject.next(1);
    subject.subscribe(x => console.log(x));
    
    Console output: 1
    
    
    @Injectable({
      providedIn: 'root',
    })
    export class LocationFilterService {
    
      private chooseLocations = new BehaviorSubject<string[]>(null);
    
      chosenLocation$ = this.chooseLocations.asObservable();
    
      chosenLocations(locations: string[]): void {
        this.chooseLocations.next(locations);
      }
    }