Categories: Angular

Enhance the presentation layer of your multi-layout Angular app

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.

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:

// 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%:

// src/styles.scss
html {
  height: 100%;
}
body {
  height: 100%;
}

Adjust the guest view

Style the guest layout by adding the wrapper class to it:

<!-- 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>
// 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:

<!-- 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:

<!-- 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:

<!-- 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:

<!-- 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>
// 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:

<!-- 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:

// 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:

<!-- 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:

// 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:

// 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');
  }));
…
// 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');
  }));
…
// 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');
  }));
…
// 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');
  }));
…
// 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:

Karma DEBUG RUNNER output

The work done in this section is contained in the commit 431b9cdd6ddd7da9e5185fc3114b4c7bd1157d08.

Photo by  JD MasonToa Heftiba on StockSnap

little_pinecone

View Comments

  • 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.

Share
Published by
little_pinecone

Recent Posts

Simplify the management of user roles in Spring Boot

Spring Security allows us to use role-based control to restrict access to API resources. However,…

3 years ago

Create a custom annotation to configure Spring Boot tests

A custom annotation in Spring Boot tests is an easy and flexible way to provide…

3 years ago

Keycloak with Spring Boot #4 – Simple guide for roles and authorities

Delegating user management to Keycloak allows us to better focus on meeting the business needs…

3 years ago

Keycloak with Spring Boot #3 – How to authorize requests in Swagger UI

Swagger offers various methods to authorize requests to our Keycloak secured API. I'll show you…

3 years ago

Keycloak with Spring Boot #2 – Spring Security instead of Keycloak in tests

Configuring our Spring Boot API to use Keycloak as an authentication and authorization server can…

3 years ago

Keycloak with Spring Boot #1 – Configure Spring Security with Keycloak

Keycloak provides simple integration with Spring applications. As a result, we can easily configure our…

3 years ago