0

I have a "HomeComponent", that displays the user name in the UI.

The user name is read from a json file using a service.

I have the service provided in the AppComponent (parent to HomeComponent) and reused in HomeComponent

AppComponent.html

<router-outlet></router-outlet>

AppComponent.ts

export class AppComponent {
constructor(private userService: UserService) {
    this.userService.fetchUserDetails();
}
}

UserService.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { User } from '../models/user';

import 'rxjs/add/operator/first';
import 'rxjs/add/operator/toPromise';

@Injectable()
export class AppStateManagerService {
   private userDetails: User;

  private initializeUser(data) {
    this.userDetails = new User();
    this.userDetails.name = data.username;
    this.userDetails.id = data.userid;
   }
constructor(private http: HttpClient) {}

async fetchDeviceDetails() {
    let response = await this.http
        .get('./app/config/user.json')
        .first()
        .toPromise();
    this.initializeUser(response);
    return this.userDetails;
}

getUserDetails() {
    return this.userDetails;
}

}

HomeComponent.html

<div>{{user && user.name}}</div>

HomeComponent.ts

export class HomeComponent {
    user: User;

    constructor(private userService: userService) {
        this.user = this.userService.getUserDetails();
    }
}

The problem I face here is, the HomeComponent gets initialized first, before the JSON parsing is complete, that is before fetchUserDetails() is complete in AppComponent, the getUserDetails() in HomeComponent is called and the user.name is null in the HTML, before being populated in the service.

Is there a way to sync this up? Without using Observable?

keerthee
  • 812
  • 4
  • 17
  • 39
  • Try resolve in routing https://angular.io/api/router/Resolve – Sreemat Sep 06 '17 at 05:21
  • will some like a {{user.name | async}} wont help here why resolve , better use this – Rahul Singh Sep 06 '17 at 05:31
  • @RahulSingh, could you clarify what you have said above? to used async? – keerthee Sep 06 '17 at 05:59
  • you return an observable and make use of async in the view which will reduce the effort for you to subscribe to observable – Rahul Singh Sep 06 '17 at 06:00
  • @keerthee Did you try the simple getter technique I demonstrated in my answer? – DeborahK Sep 06 '17 at 06:01
  • @DeborahK, Yeah I did try, it working great! But the getter is getting called 20 times! Just wanna look for other better options if possible. – keerthee Sep 06 '17 at 06:03
  • Well, you can't see it happen without a getter, but in the existing code ... the user property will be retrieved 20 times as well. So you aren't saving anything by not using a getter. – DeborahK Sep 06 '17 at 06:06
  • @DeborahK, I feel it is expensive(is it not?!), is it okay to call that much number if times, even for the first load? Is this like, getter will check for value until any new value is got? Will it take care if the value is changed in future as well? – keerthee Sep 06 '17 at 06:12
  • 1
    All that method is doing is returning a reference to your list ... so that should not be expensive. It's not re-retrieving it from the data store each time. It's calling `getUserDetails()` and not `fetchDeviceDetails()` (which would be more expensive.) – DeborahK Sep 06 '17 at 06:15

2 Answers2

2

fetchDeviceDetails() is asynchronous so i hope you can agree with me that getUserDetails() will immediately return undefined. Simple stuff right?

So how to fix this: You need to let HomeComponent know that data is available. We do that using Observables. One example is:

fetchDeviceDetails(): Observable<any> {
  return new Observable(observer => {
    this.http.get(whatever).subscribe(
      res => {
        this.initializeUser(res);
        observer.next(res);
      }
    );    
  });
}

Now you can subscribe to this event:

constructor(private userService: userService) {
  this.userService.fetchDeviceDetails().subscribe(
    res => this.user = res
  );
}
Carsten
  • 4,005
  • 21
  • 28
1

Another option is to use a getter like this:

export class HomeComponent {
    get user(): User {
       return this.userService.getUserDetails();
    }

    constructor(private userService: userService) {  }
}

This leverages Angular's change detection to ensure that the user data is set in the UI as soon as it is available.

DeborahK
  • 57,520
  • 12
  • 104
  • 129