Check out the example of a multi-layout Angular application templates complemented with Bootstrap styles.
What we are going to build
Our application provides two separate layouts – one served on a landing page view, the other used on a dashboard view. We will create a horizontal navigation and a footer for the landing page and a vertical navigation for a dashboard.
All views will be complemented with Bootstrap classes.
The finished project is available in the following GitHub repository: little-pinecone/angular-multi-layout-scaffolding.
Requirements
I try to keep the project up to date. Visit the releases list to keep track of the current versions of frameworks and libraries used. |
- We will work on an Angular 6 project that uses the
scss
preprocessor and Bootstrap styles. To learn how to create such a project, visit the How to create a new Angular project post. - The project requires the multiple layout feature applied – check out the Apply multiple layout to an Angular app post before you start working with this tutorial.
Add html and scss to components
Center the content of all pages
To center the content of all pages, update the page-content.component.scss
file:
1 2 3 4 5 6 7 |
// src/app/layout/page-content/page-content.component.scss :host { align-self: center; &.full-width { width: 100%; } } |
and set html
and body
heights to 100%:
1 2 3 4 5 6 7 |
// src/styles.scss html { height: 100%; } body { height: 100%; } |
Adjust the guest view
Style the guest layout by adding the wrapper
class to it:
1 2 3 4 5 6 |
<!-- src/app/layout/guest/guest-layout/guest-layout.component.html --> <div class="wrapper"> <app-guest-top-nav></app-guest-top-nav> <app-page-content></app-page-content> <app-guest-footer></app-guest-footer> </div> |
1 2 3 4 5 6 7 |
// src/app/layout/guest/guest-layout/guest-layout.component.scss .wrapper { display: flex; flex-direction: column; height: 100%; justify-content: space-between; } |
Add the top navigation to the guest-top-nav.component.html
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!-- src/app/layout/guest/guest-top-nav/guest-top-nav.component.html --> <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> <ul class="navbar-nav mr-auto"> <li class="nav-item active"> <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a> </li> <li class="nav-item"> <a class="nav-link" href="#">Link</a> </li> <li class="nav-item"> <a class="nav-link disabled" href="#">Disabled</a> </li> </ul> </nav> |
Create a footer by editing the guest-footer.component.html
:
1 2 3 4 5 6 |
<!-- src/app/layout/guest/guest-footer/guest-footer.component.html --> <footer class="footer bg-dark pt-2 pb-2"> <div class="container text-center"> <span><a class="text-white" href="https://keepgrowing.in/">keep_growing</a></span> </div> </footer> |
Fill the content of the landing page with the follwing example view:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<!-- src/app/pages/landing-page/landing-page.component.html --> <div class="container row"> <div class="col-md-5"> <div> <h2>A simple landing page example</h2> </div> </div> <div class="col-md-7"> <div> <h3>Lorem ipsum dolor sit amet</h3> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. </p> <ul> <li><a href="#">Feature 1</a></li> <li><a href="#">Feature 2</a></li> <li><a href="#">Feature 3</a></li> <li><a href="#">Feature 4</a></li> </ul> </div> </div> </div> |
With Bootstrap included into your project, the landing page should look like this:
The work done in this section is contained in the commit 5e2b80b4f3f7a2fba42e3d49542aa8b71f3d5a5e.
Adjust the view for an authorised user
Style the authorised layout by adding the wrapper
class to it:
1 2 3 4 5 |
<!-- src/app/layout/authorised/authorised-layout/authorised-layout.component.html --> <div class="wrapper"> <app-authorised-side-nav></app-authorised-side-nav> <app-page-content class="full-width"></app-page-content> </div> |
1 2 3 4 5 |
// src/app/layout/authorised/authorised-layout/authorised-layout.component.scss .wrapper { display: flex; flex-direction: row; } |
The full-width
class has been already defined in the page-content.component.scss
file.
Edit the authorised-side-nav.component.html
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<!-- src/app/layout/authorised/authorised-side-nav/authorised-side-nav.component.html --> <nav id="sidebar" class="navbar-dark bg-dark"> <div class="side-nav-header"> keep_growing </div> <hr> <ul class="navbar-nav"> <li class="nav-item active"> <a class="nav-link" href="#">Home page</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Link</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Link</a> </li> </ul> </nav> |
Add styles to the component:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
// src/app/layout/authorised/authorised-side-nav/authorised-side-nav.component.scss :host { background: #343a40; } #sidebar { width: 200px; min-height: 100vh; font-weight: 300; font-size: 1rem; line-height: 1.5; padding: 0; } #sidebar ul { width: 100%; } #sidebar ul li { list-style: none; } #sidebar ul li a { padding: 15px; display: block; width: 100%; &:hover { background-color: rgba(255, 255, 255, 0.1); } } .side-nav-header { display: block; padding: 15px; color: #fff; font-weight: 400; font-size: 1.5rem; text-align: center; width: 100%; } hr { border-top: 1px solid #666; margin-top: 0; } |
Edit the dashboard.component.html
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
<!-- src/app/pages/dashboard/dashboard.component.html --> <div class="container-fluid"> <h2 class="mb-5 mt-4">Dashboard page</h2> <div> <div class="dashboard-cards"> <div class="card"> <div class="card-header"> Featured </div> <div class="card-body"> <h5 class="card-title">Special title treatment</h5> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div> <div class="card"> <div class="card-header"> Featured </div> <div class="card-body"> <h5 class="card-title">Special title treatment</h5> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div> <div class="card" style="width: 18rem;"> <div class="card-body"> <h5 class="card-title">Card title</h5> <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6> <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p> <a href="#" class="card-link">Card link</a> <a href="#" class="card-link">Another link</a> </div> </div> <div class="card" style="width: 18rem;"> <div class="card-body"> <h5 class="card-title">Card title</h5> <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6> <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p> <a href="#" class="card-link">Card link</a> <a href="#" class="card-link">Another link</a> </div> </div> <div class="card" style="width: 18rem;"> <div class="card-body"> <h5 class="card-title">Card title</h5> <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6> <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p> <a href="#" class="card-link">Card link</a> <a href="#" class="card-link">Another link</a> </div> </div> <div class="card" style="width: 18rem;"> <div class="card-body"> <h5 class="card-title">Card title</h5> <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6> <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p> <a href="#" class="card-link">Card link</a> <a href="#" class="card-link">Another link</a> </div> </div> <div class="card" style="width: 18rem;"> <div class="card-body"> <h5 class="card-title">Card title</h5> <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6> <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p> <a href="#" class="card-link">Card link</a> <a href="#" class="card-link">Another link</a> </div> </div> </div> </div> </div> |
Adjust styles of the dashboard page:
1 2 3 4 5 6 7 8 9 10 11 |
// src/app/pages/dashboard/dashboard.component.scss .dashboard-cards { display: flex; flex-direction: row; flex-wrap: wrap; justify-content:flex-start; margin: 0 -15px; >.card { margin: 15px; } } |
The content of the dashboard page should look like this:
The work done in this section is contained in the commit 4b51190ea37246cfdb009b24569ad7060660ea64.
Update tests
The new html content should be accounted for in the tests. Copy the example tests from the following snippets:
1 2 3 4 5 6 7 |
// src/app/layout/authorised/authorised-side-nav/authorised-side-nav.component.spec.ts … it('should render menu in the top navigation', async(() => { const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('nav').textContent).toContain('LinkLink'); })); … |
1 2 3 4 5 6 7 |
// src/app/layout/guest/guest-footer/guest-footer.component.spec.ts … it('should render brand name in a footer tag', async(() => { const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('footer').textContent).toContain('keep_growing'); })); … |
1 2 3 4 5 6 7 |
// src/app/layout/guest/guest-top-nav/guest-top-nav.component.spec.ts … it('should render menu in the top navigation', async(() => { const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('nav').textContent).toContain('LinkDisabled'); })); … |
1 2 3 4 5 6 7 |
// src/app/pages/dashboard/dashboard.component.spec.ts … it('should render main header in a h2 tag', async(() => { const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('h2').textContent).toContain('Dashboard page'); })); … |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// src/app/pages/landing-page/landing-page.component.spec.ts … it('should render main header in a h2 tag', async(() => { const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('h2').textContent).toContain('A simple landing page example'); })); it('should render sub header in a h3 tag', async(() => { const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('h3').textContent).toContain('Lorem ipsum dolor sit amet'); })); it('should render description in a p tag', async(() => { const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('p').textContent).toContain('Lorem ipsum dolor sit amet, consectetur adipiscing elit,'); })); it('should render feature list', async(() => { const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('ul').textContent).toContain('Feature 1Feature 2Feature 3Feature 4'); })); … |
Run the following command: $ ng test and check out the output:
The work done in this section is contained in the commit 431b9cdd6ddd7da9e5185fc3114b4c7bd1157d08.
Photo by JD MasonToa Heftiba on StockSnap
thank you so much for this awesome tutorial. This is really big helpful for me to making Admin panel with multiple layout format. Thanks a lots of you.