Search code examples
angularserviceangular2-observablesbehaviorsubject

Passing the data between components using service method in Angular


I am using a angular service to pass the data between components. I would like this service to hold the 4 ids uniId, schoolId, classId and studentId. There are 4 components: Component 1 setting uniId, Component2 setting schoolId using uniId, Component 3 setting classId using uniId and schoolId and Component 4 setting studentId using uniId, schoolId and classId. I want to achieve following:

  1. Setting all the 4 ids into the service.
  2. Using service, get the ids on the view of the component and use it for navigation in the form of Component1 Path -> Component2 Path -> Component3 Path -> Component4 Path using the anchor tag (so that when I am on page 3 I can navigate to page 2 using the id from service.)

Model class

export interface GlobalData {
    uniId: number;
    schoolId: number;
    classId: number;
    studentId: number;
}

Service Class

import { Injectable } from '@angular/core';
import { GlobalData } from 'src/models/GlobalData';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
@Injectable({
  providedIn: 'root'
})
export class GlobaldataService {
  private globalDataSubject = new Subject<GlobalData>();

  constructor() { }

  public getGlobalData(): Observable<GlobalData> {
    return this.globalDataSubject.asObservable();
  }

  public setGlobalData(globalDataObj: GlobalData) {
    return this.globalDataSubject.next(globalDataObj);
  }

}

Component 3

globalDataObject: GlobalData;
uniId: number = 0;
schoolId: number = 0;
constructor(private router: Router, private route: ActivatedRoute, private globalDataService: GlobaldataService) {
  }
ngOnInit() { 
this.route.queryParams.subscribe(params => {
        this.uniId = params['uniId'];
        this.schoolId = params['schoolId'];
      })
      this.globalDataService.setGlobalData({
        uniId: this.uniId, 
        schoolId: this.schoolId,
        classId: 0,
        studentId: 0
      });
      this.globalDataService.getGlobalData().subscribe(x =>    
        this.globalDataObject = x) 
}

Component 3 View

<li class="dds__breadcrumb__item" [routerLinkActive]="['link-active']">
            <a [routerLink]="['/unis']">Universities</a>
        </li>
<li class="dds__breadcrumb__item" [routerLinkActive]="['link-active']">
            <a [routerLink]="['/schools']" [queryparams]="{uniId: this.uniId}">Schools</a>
</li>
<li class="dds__breadcrumb__item" [routerLinkActive]="['link-active']">
            <a [routerLink]="['/classes']">Classes</a>
        </li>

In Component 3, when I click on first link it would show all unis, when I click on the second link it would show all schools in the uni whose id is selected and classes would npt navigate to anything as Component 3 is Classes.

I am not getting data in service and the navigation does not concatenate ids. Can anyone please help on this.


Solution

  • Three problems:

    1. You're setting your global data outside of your params subscription.

    2. Observable subscriptions don't trigger change detection unless you're using the async pipe. So you need to force it with ChangeDetectorRef.

      constructor(private changeDetectorRef: ChangeDetectorRef) {}
    
      async ngOnInit() {
        this.route.queryParams.subscribe((params) => {
          this.uniId = params['uniId'];
          this.schoolId = params['schoolId'];
          this.globalDataService.setGlobalData({
            uniId: this.uniId,
            schoolId: this.schoolId,
            classId: 0,
            studentId: 0,
          });
          this.globalDataService.getGlobalData().subscribe((x) => {
            this.globalDataObject = x;
            this.changeDetectorRef.detectChanges();
          });
        });
      }
    
    1. queryParams has a capital P
    ...
    <li class="dds__breadcrumb__item" [routerLinkActive]="['link-active']">
      <a [routerLink]="['/schools']" [queryParams]="{ uniId: this.uniId }"
        >Schools</a
      >
    </li>
    ...
    

    This can all be rewritten much simpler, there's no need for observables if you're simply setting and getting values.

    Service

    @Injectable({
      providedIn: 'root',
    })
    export class GlobaldataService {
      globalData?: GlobalData;
    }
    

    Component 3

      get uniId() {
        return this.globalDataService.globalData?.uniId;
      }
    
      constructor(
        private route: ActivatedRoute,
        private globalDataService: GlobaldataService
      ) {}
      ngOnInit() {
        const params = this.route.snapshot.queryParams;
        this.globalDataService.globalData = {
          uniId: params['uniId'],
          schoolId: params['schoolId'],
          classId: 0,
          studentId: 0,
        };
      }
    
    <li class="dds__breadcrumb__item" [routerLinkActive]="['link-active']">
      <a [routerLink]="['/unis']">Universities</a>
    </li>
    <li class="dds__breadcrumb__item" [routerLinkActive]="['link-active']">
      <a [routerLink]="['/schools']" [queryParams]="{ uniId }">Schools</a>
    </li>
    <li class="dds__breadcrumb__item" [routerLinkActive]="['link-active']">
      <a [routerLink]="['/classes']">Classes</a>
    </li>
    

    Although I don't see a point in passing queryParams through the template when you can just inject the service.