1

We have a User component that receives a user object as an @input:

export class UserComponent implements OnInit {
  @Input() user: User;

  constructor() {}
}

Here the corresponding template:

<div class="user-box">
  <img class="avatar" [src]="user.avatar" alt="user_picture">
  <div class="infos">
    <div class="username">{{ user.username }}</div>
    <div>{{ user.email }}</div>
    <div class="gender">{{ user.gender }}</div>
  </div>
</div>

In a route component UsersList (primary <router-outlet>) we need to display a list of users to the screen:

<user
  *ngFor="let user of users
  [user]="user"
  (click)="selectUser(user)">
</user>

Then, when we click to select a user from the list, we show the selected user data using the same User component but this time as a route component (named <router-outlet name="selected">):

Application Mockup

Here our routes:

const ROUTES: Routes = [
  {
    path: '',
    component: UsersList
  },
  {
    path: ':username',
    component: UserComponent,
    outlet: 'selected'
  }
];

So, here the selectUser() function:

selectUser(user: User) {
  return this.router.navigate([ { outlets: { selected: [ user.username ] } } ], { state: { user } });
}

And inside our User component, we need to implement the OnInit interface:

if (this.route.outlet === 'selected') {
  this.route.paramMap.pipe(map(() => window.history.state)).subscribe(state => {
    if (!state.user) {
      return this.router.navigate([ { outlets: { selected: null } } ]);
    }
    this.user = state.user;
  });
}

All of this works perfectly fine! What I am wondering though is if it's a good practice to do it this way?

FYI, I have also tried to use a route resolver but I can't have access to the NavigationExtras state object... The only way to get the user data inside a resolver is to pass it as queryParams. It can be fine when we have 3 properties, but, if we have 10+ properties, the URL becomes ugly and messy. And also, it seems that a resolver role is to resolve asynchronous data and not passing static data.

Thanks for giving your insights!

samteb
  • 63
  • 9

1 Answers1

1

The good practice, or let's say "the Angular way", is to use route resolvers how is written in the documentation: https://angular.io/guide/router#fetch-data-before-navigating.

I use resolvers when I can, so I can write clean page components that get the data they need from the router as they are instantiated (I can also re-use the resolver for other routes, but that rarely happens).

This makes also the routes independent from each other: I can see the user page before going into the user list page, I don't think I can do that with your current code. Plus, if the user does not exist, I could redirect to the 404 page from the resolver directly.

By the way, resolvers could have some drawbacks to keep in mind:

  • if you work with real-time async data, you lose the real-time update because the document if fetched just once (usually by take(1)) and the stream gets closed
  • if you're using something like Ionic, which caches pages, you could have issue with outdated data
michelepatrassi
  • 2,016
  • 18
  • 32
  • The main difference here is that I don't have a "user page". I show the user using an auxiliary route, on the right side of the page. So, you will never see "the user page" before "the user list page". You see both at anytime you click on one of the user. – samteb Nov 27 '19 at 05:10
  • 1
    ah ok got it! So in this case here's what I would do: use a resolver for the list page to get all the users and use a simple Master/Detail pattern (https://angular.io/tutorial/toh-pt3) to display the user. If you want to be able to select a user from the url, I would then add the user id as param or queryParam and let the list page select the right user and pass it to the user component. In this way, the user component does not need to use the routing and became reusable. – michelepatrassi Nov 27 '19 at 09:40
  • I have done that but without adding the username as a queryParam. I will definitely give it a try and get back to you. Thanks. – samteb Nov 27 '19 at 09:52