2

Follow up question to Share data between components using a service in Angular2 and a response I got.

I'm trying to share data that a service providers retrieves via a GET. The component structure is as follows with the main routes going to CustomerPortalDashboardComponent and DocumentsComponent

<CustomerPortalBaseComponent>
 <router-outlet>
  <CustomerPortalDashboardComponent/> 
  <DocumentsComponent/>
 <router-outlet>
</CustomerPortalBaseComponent>

CustomerPortalBaseComponent makes a call to get user info:

ngOnInit() {
this.customerPortalUserService.getUserInfo()
  .subscribe(
    (event: HttpEvent<any>) => {
      switch (event.type) {
        case HttpEventType.Sent:
          break;
        case HttpEventType.Response:
          this.handleUserData(event.body)

      }
    },
    (err: HttpErrorResponse) => {
      if (err.error instanceof Error) {
        console.log("Client-side error occured.");
      } else {           
        console.log("Server-side error occured.");
      }
    }
  )
}
 handleUserData(data: any){
  this.userData = data;
}

CustomerPortalUserService

getUserInfo(){
interface ItemsResponse {
  results: string[];
}
return  this.http.get<ItemsResponse>(this.userUrl,  {
  observe: 'response',
  headers: new HttpHeaders().set(),
})
}

What is the best way to get the userData in CustomerPortalDashboardComponent and DocumentsComponent and ensure that the data has been retrieved before the other components try and get it?

Anthony
  • 2,330
  • 8
  • 44
  • 64

2 Answers2

1

Several points to make it work :

  1. To make sure the data is loaded before the routes are reached, you can use a resolver. The resolver will be linked to your route paths, and will be a function that will execute the Http request before the route is resolved.
  2. You need to store your data in a store once the call has been made. That can be done with external stores such as @Ngrx, but the most basic example is to use a variable inside your service (even better with getter / setter) :

example service :

_equipmentDetails: any  // Should be private in use-case

...

getEquipmentDetail(clientId, modemId): Observable<any> {
  const url = 'myOwnUrl';

  return this.Http
    .get(url)
    .map(result => {
      if (result) {
        // Notice how I set the value from the Http call.
        return this._equipmentDetails = result.json();
      }
    })
    .catch(this.handleError);
}

And then you can get the property value from your components through dependency injection.

  constructor(
    ...
    private equipmentService: ServiceClass,
  ) {
    this.equipmentService._equipmentDetails;
  }
Alex Beugnet
  • 4,003
  • 4
  • 23
  • 40
1

Solved this by using a shared service. I was doing this before BUT in my shared service I was using a Subject instead of a BehaviorSubject. The Subject does not cache data so I thought my design pattern was not working. When changed to a BehaviorSubject everything worked perfectly.

CustomerPortalBaseComponent makes all the HTTP calls for data that will be shared across this app module. It then sends the message to shared service and the other components (main components for route) subscribe to that service. EX:

CustomerPortalBaseComponent

this.applicationsMessagingService.sendMessage(data)

ApplicationsMessagingService:

Injectable()
export class ApplicationsMessagingService {

  private subject = new BehaviorSubject<any>(null);

  sendMessage(message: {}) {
   this.subject.next(message);
  }

  clearMessage() {
   this.subject.next(null);
 }

  getMessage(): Observable<any> {
   return this.subject.asObservable();
  }

}

CustomerPortalDashboardComponent

ngOnInit() {
this.subscription = this.applicationsMessagingService.getMessage().subscribe(message => {
  this.message = message;
  this.handleApplicationData(this.message);
});


handleApplicationData(data: any){
  if (data){
   this.applicationData = data;
 }
}

applicationData is then passed to child components and received via @input

Anthony
  • 2,330
  • 8
  • 44
  • 64