2

I created my Angular app with Angular CLI but now I have a problem with router. The app I want to build has some pages that have header and footer but also some pages that don't, like login page for example. So I need a way to share the layout with header and footer for the pages which need it and to use other layout for the ones that don't. What's the right approach for this?

My first idea was to have one route with header and footer which would be parent to all other routes which need it, but I can't find a way to share the same parent across multiple routing modules. The only way to specify parent child relation I was able to find in the docs is through a list of children when defining a route but then I need to have all the routes defined in the same place which I'd like to avoid.

Second idea was to use secondary routes and I tried with defining app.component.html with primary outlet for content and two secondary outlets for header and footer but then I wasn't able to access secondary outlet from the feature routing module.

Third idea was to have parent route with the same component for each page that has header and footer. That shared component would specify header and footer but the problem here is that then header and footer would be instantiated each time user navigates to a different feature so that would reset their state.

With ui-router I was using in AngularJS this was really easy to do with named views and ability to specify shared parent state. I'd really appreciate pointing in the right direction here.

jbojcic
  • 954
  • 1
  • 11
  • 25
  • Code? Where​ is it? – Aravind Apr 21 '17 at 16:53
  • 1
    Yea, I had the same question here http://stackoverflow.com/questions/43469401/manage-different-base-layouts-in-angular2, still no proper aswer – Leguest Apr 21 '17 at 17:00
  • 1
    @Aravind which part you'd like to see? – jbojcic Apr 21 '17 at 17:03
  • You need to start with a primary outlet at `app.component` and have a component on that main route that have a secondary outlet `main-component` (for example) and you just place your header & footer in your `main-component`, so that any chilld routes will have a header and footer. If you need several components without the header & footer, you just place a `second-component` with another router-outlet and change route accordingly. – Alex Beugnet Apr 21 '17 at 17:50
  • Regarding your last paragraph ... you can do named views with secondary router outlets and you can share state using a service. – DeborahK Apr 21 '17 at 19:18
  • @jbojcic did you ever solve this without modifying the `children` in parent routing module? – micadelli Oct 15 '19 at 15:08
  • @micadelli check my blog post here https://blog.angularindepth.com/angular-routing-reusing-common-layout-for-pages-from-different-modules-440a23f86b57 – jbojcic Oct 16 '19 at 09:27
  • @jbojcic I see, thanks... the project i'm working on, is way too big for to use `children` in one routing module. I guess I need to settle for route events :) btw, i've read that article few months ago, and never figured it out it was you, the op – micadelli Oct 16 '19 at 11:27

2 Answers2

0

Would something like this work?

-- App Component (with router outlet)

---- Parent component (with header, footer and child router outlet)

------ Child components to be displayed within the parent component's router outlet

---- Other components (such as the login with no header or footer)

DeborahK
  • 57,520
  • 12
  • 104
  • 129
  • But if I am not wrong in this case all child components need to be defined in the same routing module as the parent, right? – jbojcic Apr 21 '17 at 21:02
  • No, child components don't need to be defined in the same routing module as the parent. – DeborahK Apr 21 '17 at 23:13
  • So if I have two features featureA and featureB which both have header and footer and I have their routing modules, featureA-routing.module and featureB-routing.module. Each of the routing modules specifies it's own routes like in https://angular.io/docs/ts/latest/guide/router.html#!#routing-refactor. So how do I set them to share the same parent? Do you mean to have something like: { path: '', component: ParentComponent, children: [ ... all other routes for feature A ...] }, and { path: '', component: ParentComponent, children: [ ... all other routes for feature B ...] }, in each module – jbojcic Apr 21 '17 at 23:26
  • This code is difficult to read in these comments. Can you add something like this to the plunker? (Or is it already?) I'm moving my office to a new space this week-end ... so I'll most likely be offline most of the week-end. I can look at this more later next week. – DeborahK Apr 22 '17 at 00:40
  • Are you using lazy loading? – DeborahK Apr 22 '17 at 00:48
  • Not sure how to run this in plunker but here is github link: https://github.com/jbojcic1/angular-routing-example . Is this what you are suggesting? Also I am not using lazy loading currently – jbojcic Apr 22 '17 at 10:46
  • It is possible with lazy loading syntax. I'll take a look at your plunker next week when my system is put back together. – DeborahK Apr 23 '17 at 04:02
  • did you mean something like this https://stackblitz.com/github/jbojcic1/angular-routing-example?file=src%2Fapp%2Flayout%2Flayout-routing.module.ts – jbojcic Apr 02 '18 at 14:40
0

You can subscribe to the router inside your footer component and just hide it based on the route.

1) the easiest hack-ish way would be to simply hide it

footer.component.ts

@HostBinding('style.display')
display :string = 'block';

constructor(private router: Router) {}

ngOnInit() {
  this.router.events.subscribe((val) => {

  if (val instanceof NavigationEnd) {
    this.display = 'block';
    if (val.url == '/hidefooterroute') {
      this.display = 'none';
    }
  }
 ...

2) Another way you can do this is to stick params in route.data and subscribe inside the main app or a service.

someRoutes = [
  { path:'awesome', data:{ showFooter: false, title: 'This is page is too awesome for a footer'}, loadChildren: "./awesome.module#awesomeModule" }  ];

...

this.router.events
  .filter(event => event instanceof NavigationEnd)
  .map(() => this.activatedRoute)
  .map(route => {
    while (route.firstChild) route = route.firstChild;
    return route;
  })
  .filter(route => route.outlet === 'primary')
  .subscribe((route) => {
    // do things with route
    route.data.subscribe((data) => {
      // do things with route data
      this.showFooter = data.showFooter;
    });
  });
Dylan
  • 4,703
  • 1
  • 20
  • 23
  • All of this would work but this is a hack. This is very common case and imo should be possible with router. – jbojcic Apr 21 '17 at 21:09
  • Well `hack` is a relative term here, if you want to show/hide a small piece of content *ngIf and are made to do just that, so all you would need is the route to provide the boolean. There is a router in the works - https://ui-router.github.io/ng2/ - but I would feel safer with the `hack` for now, imo. – Dylan Apr 21 '17 at 23:12