This project shows a simple example for a classic header - content - footer layout using Angular.
I had the following goals in mind when creating this project:
- Header and footer shall always be visible and never resize
- The content area in the middle shall always stretch vertically to use all the remaining vertical space
- Show a scrollbar when the content area exceeds available vertical space
- The HTML body shall never show a scrollbar
This project was generated with Angular CLI version 7.3.6.
At first sight the problem sounds rather trivial, especcially with pure HTML and CSS. The whole problem became rather challenging in combination with Angular and routing. There are tons of articles, blogs, stackoverflow questions, and code examples describing the same problem. But none of these examples seemed to fully work for me.
Overall there are two basic challenges:
- How to stretch the content area vetically if the content is smaller than the available space
- How to show a scrollbar in the content area when the content overflows the available vertical space
It seems these days the two common approaches for a nested layout with columns and rows are:
- CSS Grid
- CSS Flexbox
I chose CSS Flex because I already had some experience with it. Angular supports Flex through CSS but also via Angular flex-layout. Both approaches work fine.
There are a few key points which made the solution work:
To avoid vertical srollbars at the HTML body I had to disable overflow at the top level styles.css:
html, body {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
A component within a flex box will stretch with 'flex: auto' applied. But this only works when flex layout will be applied to all parent components!
Component hierarchy in this example:
body
app-root
router-outlet
app-content
content-wrapper
dynamic-content
Flex box style must be applied to the router-outlet via the host property in app.component.css:
:host {
display: flex;
flex-direction: column;
flex: auto;
height: 100%;
}
Height 100% makes sure that the next child component can stretch vertically.
File content.component.html contains the actual content (a list of h1 lines) surrounded by a wrapper div element. The wrapper div element makes sure that the content can be scrolled vertically. The content component itself will be loaded into the angular router-outlet. Similar to the router-outlet itself, flex box layout must be applied to the component's host property.
File content.component.css:
:host {
display: flex;
flex-direction: column;
flex: auto;
}
.wrapper {
height: 0;
}
.content {
background: lightgray;
border: 1px;
border-style: solid;
height: 100%;
overflow: auto;
}
Overflow auto applied at the content div makes sure a scrollbar will be shown when the content exceeds available space.
CSS 'height: 0' (or min-height) applied to the wrapper element is rather a mystery, but without it doesn't work! I assume this has to do with the general rule, a scrollbar can only be shown for an element which as a defined height (even if it is zero). The actual available vertical space for the content component is automatically derived from the brwoser's current viewport (that was the goal in the first place). Nevertheless, the solution only worked by applying a height property to the wrapper div itself! I also tried it without a wrapper div by applying the same settings directly to the host element, but this didn't work either.
Run ng serve for a dev server. Navigate to http://localhost:4200/. The app will automatically reload if you change any of the source files.
Run ng generate component component-name to generate a new component. You can also use ng generate directive|pipe|service|class|guard|interface|enum|module.
Run ng build to build the project. The build artifacts will be stored in the dist/ directory. Use the --prod flag for a production build.
Run ng test to execute the unit tests via Karma.
Run ng e2e to execute the end-to-end tests via Protractor.
To get more help on the Angular CLI use ng help or go check out the Angular CLI README.