Boaris
Boaris

Reputation: 5226

How to not pass header and footer into each page

My website will have a sidebar, header, footer, and content. Something like this: enter image description here That's an easy grid I can implement.
But on some pages I want my content to have an additional sidebar on the right. The problem is that I want it to move the header and the footer: enter image description here The only solution that I found is to pass the header and the footer into each page. But this is not DRY.

So, how can I do that? Is this even possible? If so, how? I can't even imagine how the final code should look like...

P.S. I read about auxiliary outlets, but don't know how can I apply them to this issue.

Upvotes: 1

Views: 893

Answers (3)

spierala
spierala

Reputation: 2699

Here another answer after I understood your problem a bit better :)

Look at this codepen: https://codepen.io/spierala/pen/wvBWaWV

For the CSS I also used bootstrap flex classes, but the main magic is position: absolute on the div.content-sidebar so it can "escape" of its parent container). Also the parent container div.content-container is not allowed to have positioning itself to make that work.

The app-component must decide over giving footer and header a margin.

The routed component inside the router outlet must decide over the margin of the div.content. Because div.content-sidebar is absolute positioned it can not influence the width of div.content anymore. We have to fix that manually by adding margin if the content-sidebar is visible.

So you can use Angular to add the css classes / styling which are needed for adding or removing the margins in app-component and in the routed components - depending on the content-sidebar visibility.

How will app-component and routed component know? You could introduce a Angular service which will hold the information if a sidebar is visible or not. The routed component could set that information. app-component reads it.

EDIT: regarding the "escape"... if header and footer have fixed heights then you could use negative margins on the content-sidebar instead of the position:absolute approach

If footer and header have a height of 50px then the div.content-sidebar margins look like this:

.content-sidebar {
  margin-top: -50px; // - header height
  margin-bottom: -50px; // - min footer height
}

I created another codepen here: https://codepen.io/spierala/pen/PowzmzL You will also see that div.content does not need margin anymore since the content-sidebar is not positioned absolute.

Upvotes: 2

spierala
spierala

Reputation: 2699

You can also add a data property to your routes (see more info here: https://angular.io/guide/router):

const routes: Routes = [
  {
    path: 'comp1',
    component: Comp1Component,
    data: { showContentSideBar: true }
  },
  { path: 'comp2',
    redirectTo: '/Comp1Component',
    data: { showContentSideBar: false }
  },
  { path: '',
    redirectTo: '/comp1',
    pathMatch: 'full'
  },
];

In your app-root component you can read that data with the showContentSideBar flag like this:

  constructor(
    private router: Router,
    private activatedRoute: ActivatedRoute
  ) {
  }

  ngOnInit() {
    // https://ultimatecourses.com/blog/dynamic-page-titles-angular-2-router-events

    this.router.events.pipe(
      filter((event) => event instanceof NavigationEnd),
      map(() => this.activatedRoute),
      map((route) => {
        while (route.firstChild) route = route.firstChild;
        return route;
      }),
      filter((route) => route.outlet === 'primary'),
      mergeMap((route) => route.data))
      .subscribe((event) => {
        console.log(event); // Outputs {showContentSideBar: true} or {showContentSideBar: false}
        // write showContentSideBar to a public var and use it for ngIf on sideBar component
      });
  }

app.component template

    <sidebar></sidebar>
    <div class="main">
      <header></header>
      <div class="content><router-outlet></router-outlet></div>
      <footer></footer>
    </div>
    <content-sidebar *ngIf="showContentSideBar"></content-sidebar>

That way you can simply ngIf the content-sidebar in your app (root) component using the information that you get in the subscribe. Only one root component/template will be needed for that approach.

Sadly the code to read the data from the route looks a bit bloated, but as far as I know there is no better solution for that. See also on Github: https://github.com/angular/angular/issues/9662

Todd Motto´s solution: https://ultimatecourses.com/blog/dynamic-page-titles-angular-2-router-events

Upvotes: 0

bryan60
bryan60

Reputation: 29335

you'd have your wrapper 1 component:

@Component({
  selector: 'wrapper-one',
  template: `
    <sidebar></sidebar>
    <div class="main">
      <header></header>
      <div class="content><router-outlet></router-outlet></div>
      <footer></footer>
    </div>
  `
})
export class WrapperOneComponent { ... }

then wrapper 2:

@Component({
  selector: 'wrapper-two',
  template: `
    <sidebar></sidebar>
    <div class="main has-content-sidebar">
      <header></header>
      <div class="content><router-outlet></router-outlet></div>
      <footer></footer>
    </div>
    <content-sidebar></content-sidebar>
  `
})
export class WrapperTwoComponent { ... }

then structure your routes:

[
  {
    path: '',
    component: WrapperOneComponent,
    children: [
      // all not needing sidebar
    ]
  }
  {
    path: '',
    component: WrapperTwoComponent,
    children: [
      // all needing sidebar
    ]
  }
]

Upvotes: 1

Related Questions