2

I have a component 'panel' which is having two child routes dashboard and holidays. Both these child routes are getting a data on their ngOnInit from parent component service.

Everything is working fine and dashboard is loading with data if we are going in normal way, but if we are reloading the child route its not getting the parent service data.

How to solve this issue?

Routing module:-

{
    path: 'panel',
    component: PanelComponent,
    canActivate: [AuthGuard],
    children: [

        {
            path: 'dashboard',
            component: DashboardComponent,

        },
        {
            path: 'holidays',
            component: HolidaysComponent
        }
    ]
}

panelComponent.ts:-

export class PanelComponent implements OnInit {

constructor(private panelService: PanelService, route: ActivatedRoute, private router: Router) {

}

ngOnInit() {
    this.panelService.getUserProfile().subscribe(
        res => {
            this.panelService.userDetails = res;
            this.router.navigate(['panel/dashboard'])
        },
        err => {
            console.log(err);
        },
    ); 
}

DashboardComponent.ts:-

export class DashboardComponent {
/** dashboard ctor */

private userDetails = {
    name:""
}
constructor(private panelService: PanelService) {

}
ngOnInit() {
    //console.log(this.panelService.userDetails.profileDetails[0].name);
    this.userDetails.name=this.panelService.userDetails.profileDetails[0].name
}
Ameerudheen.K
  • 657
  • 1
  • 11
  • 31

3 Answers3

2

It's good to share data between the components when these components are getting rendered for single route.

Everything is working fine

In this case you are child components will render after the Parent component, but sometimes you need to refresh from the child route then you need to call the service method itself from the child component.

I think you need refactor your child component something like below

ngOnInit() {
  if(!this.panelService.userDetails) { 
     this.panelService.getUserProfile().subscribe( res => {
        this.panelService.userDetails = res;
      },
      err => {
        console.log(err);
      }); 
  }
  this.userDetails.name = this.panelService.userDetails.profileDetails[0].name;
}

Edit

BehaviorSubject is good approach for your requirement. But but if you are reloading the child route itself, there maybe a chance that you will lose the data in behaviorSubject oject too.

If you think you need to have the data even child route get refreshed, checking the data from the Child component is better approach.(at least in my opinion).

Ganesh
  • 5,808
  • 2
  • 21
  • 41
  • after router.navigate(['panel/dashboard']), the ngOnit of dashboard , works again? – Ameerudheen.K Jun 06 '19 at 11:25
  • Hi, your solution was working fine, but i have a doubt. did parent component service not get called while child is reloaded? or it's simply because parents ajax calls are not completed, so that iam not getting data to child? your solution is working fine but now iam rewriting codes in all components to check if parent component data is present or not. is it a good way? – Ameerudheen.K Aug 20 '19 at 13:11
  • yes I can attain same functionality with Behaviour subject but as per ur opinion if child is reloaded by data from parent I must check if data presents or not? – Ameerudheen.K Aug 21 '19 at 05:58
1

ngOnInit is called when a component is created. Probably when you first visit the problem URL, both components are being created but when you revisit only the child is being created because the parent was never never destroyed. Check when ngOnInit and ngOnDestroy is being fired for the 2 components.

You may need to refactor your code.

danday74
  • 52,471
  • 49
  • 232
  • 283
  • actually i have a ajax response from parent component that is used in all child components. is it wrong sharing that through a service? how can i proceed with this scenario? – Ameerudheen.K Jun 06 '19 at 11:04
  • you could put the calls to the service in the child component (and if desired cache the response) - or you could make the request in a resolver - or Neils answer looks promising which is documented here - https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service – danday74 Jun 06 '19 at 12:42
1

You would be better to use a BehaviorSubject in your service and subscribe to its observable, your service would look like:

export class PanelService {
  private userDetailsSubject = new BehaviorSubject(null);
  userDetails$: Observable<UserDetail> = this.userDetailsSubject.asObservable();
}

Then change your getUserProfile function

getUserProfile() {
  return this.http.get(‘uri-to-server-resource’).pipe(
    tap(res => this.userDetailsSubject.next(res))
  );
}

And your PanelComponent

export class PanelComponent implements OnInit {

  constructor(private panelService: PanelService, route: ActivatedRoute, private router: Router) {
  }

  ngOnInit() {
    this.panelService
      .getUserProfile().subscribe(
        res => {
          this.router.navigate(['panel/dashboard'])
        },
        err => {
         console.log(err);
        },
    ); 
  }

Your DashboardComponent will then subscribe to the observable on the service

this.panelService.userDetails$.subscribe(value => this.userDetails = value);

Or use directly in your template

{{panelService.userDetails$ | asynchronous}}
Neil Stevens
  • 3,534
  • 6
  • 42
  • 71
  • I will try this. but did this initiate ajax calls on each child route loading, if iam subscribing? i have did like above way for avoding that – Ameerudheen.K Jun 06 '19 at 11:35
  • No you still initiate the Ajax call in your parent calling `getUserProfile`, the response is the scheduled on the `BehaviorSubject` then whenever the child subscribes to `panelService.userDetails$` they will receive the latest value – Neil Stevens Jun 06 '19 at 11:38
  • I didnt understand how you are using "this.userDetailsSubject.next(res);" this inside service file? my subscription to this service is done inside panelComponent.ts and res is getting there. – Ameerudheen.K Jun 07 '19 at 04:22
  • i have tried this way just now but still iam getting null at initial rendering of app. can u please have a look at this question [link](https://stackoverflow.com/questions/56488381/ngoninit-of-child-component-executing-before-completing-service-in-parent-compon/56488394#56488394) this seems to be the issue. – Ameerudheen.K Jun 07 '19 at 05:33
  • Yes, that's correct it will be `null` until the async HTTP call has responded. When you create the `BehaviorSubject` you provide the constructor with an initial value (in the case above that is `null`, the next value will be the response from the server, the link above will allow you to do the same from a resolver which will complete before navigating to your parent route – Neil Stevens Jun 07 '19 at 11:15