Simple Sliding Side Bar for your Angular Web Apps

Roman Jaquez
ITNEXT
Published in
12 min readAug 27, 2020

--

A Series of Web Components that you can create in Angular and avoid having to import a whole library for it. In this post, I’ll be creating a simple sliding side bar for your Angular Web Apps and PWAs (Progressive Web Apps) that you can use to hide your menu options and trigger from anywhere in the app.
Let it slide, let it slide, let it slide!.

Dependencies: Angular CLI

Let’s build a simple yet flexible and configurable sliding bar in Angular (usually used for navigation purposes) while at the same time we learn some important concepts about:

  • Creating configurable components using Angular Inputs to increase their flexibility and reusability
  • Simple Templating techniques for component embedding
  • Using services to enable highly decoupled communication between components
  • Using CSS in conjunction with Angular directives to create minimal-code animated transitions for your Angular Components

The Goal

We want to create a simple sliding bar that has the following capabilities:

  • Allow users to provide an option to either slide from the left or right
  • Allow users to configure the speed at which the sliding bar animates
  • Allow users to configure the width of the sliding bar
  • Allow users to configure the content of the sliding bar

Let’s start!

Creating the Project

Go ahead and create the project via the Angular CLI by using the command

ng new simple-side-nav

Select YES on adding Angular Routing and pick SCSS as your stylesheet format at the prompts provided during project creation.

I’m gonna be using a Google Font for this project so it doesn’t look so boring and also I’ll be using some Material Icons for Web (i’ll be using some icons from it) by referencing them from a CDN. Place the links below inside of the <head> tag of the project’s index.html. We’ll show how to use them later.

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Alata&display=swap" rel="stylesheet">

Your app’s main index.html should look like this:

At the root of the project, inside the src/app folder, I’ll be creating several directories for better project structure. I’ll create directories called components, models, pages and services. Your structure should look as follows (from the src root):

- src
- app
- components
- pages
- services

Set Up the Foundation

We already decided that we will have a few pages and a few components in this application, as well as routing, so we need to clean up the existing application component page (generated during project creation) to accommodate this change. Go to the app.component.html file, clear the boiler plate content and replace it with this:

We’re using the <router-outlet> to tap into Angular’s routing framework to show the pages through. We have two placeholders (for the side nav bar and for a header — we’ll come back later to those). Now, navigate to the app.component.scss and replace it with the following content:

At the root of the project, inside the src folder, the project generates a styles.scss file. This file is used for styles that we want to have applied at the global level and affect all pages — always be cautious on styles you place here. In our case we want to make the body tag stretch to the edges, as follows:

I’m using the strategy of making the outermost container above (in our case, the body tag) absolute positioned and stretching to the edges of the page (by setting top, right, bottom and left to 0) so child pages can stretch to the fullest and adopt their parent container’s height.

Create the Service

We need a way for the application to keep track of the state of the side nav bar (when it’s opened or closed), and provide an API to allow interested components to tap into functionality like subscribing to changes on the side nav bar’s state, and methods to change the state of the bar (trigger it to open / close programmatically), so let’s create a service to handle this.

Navigate to the src/app/services, and execute the Angular CLI command to create a service:

> ng generate service navigation
...or the short version:
> ng g s navigation

This will create a service called NavigationService, that will be injectable by default and available via dependency injection. Let’s replace its contents with the code below:

Let’s dissect the service. Notice at the top we’ve created a BehaviorSubject private property called showNav$. This is just a simple BehaviorSubject property that will allow users to subscribe to it via the API provided in this service so they can access it, subscribe to it and receive notifications as soon as this property changes. This is of type boolean so it’s just pretty much a simple on-off flag, open-close state. We’re creating this service very encapsulated, hence provided the following entrypoints around it:

  • getShowNav: this method exposes the BehaviorSubject showNav$ as an Observable so client can subscribe to it and get notified when it changes via the .asObservable() method of the BehaviorSubject.
  • setShowNav: sets the showNav$ to the value provided via the showHide parameter available.
  • toggleNavState: simply toggles the value (from true to false and vice versa).
  • isNavOpen: queries the property for its current state (whether the showNav$ is true or false).

I always like to create methods that wrap around Observables and BehaviorSubject properties — and I suggest this for testability purposes (way beyond this topic) and of course to provide a single entry point into these properties, hence the reason why I create it as private so the only way to access them is via the methods provided.

Creating the Components

Let’s start with the main component — the side nav bar — by issuing the Angular CLI command:

> ng generate component side-nav

This creates the three pieces we need for this component (HTML, SCSS and TS files). We’ll get back to these in a moment, but first we want to create an additional file. Inside the components/side-nav folder, I’ll create an additional file called side-nav-direction.ts, which will hold the enum SideNavDirection — the enumeration that will represent the two options for the side nav bar sliding animation: left and right. Add the following content to it:

Now, let’s get back to the side nav bar component. Let’s start with the Typescript logic for it. In the generated file side-nav.component.ts, let’s drop the below content:

Let’s dissect this component. This component obviously needs to be in sync with the state we’re maintaining in its companion service NavigationService, hence the reason why we’re injecting it. We’re calling the NavigationService’s getShowNav() so we can subscribe to the showNav$’s Observable that gets returned.

We are providing four Input properties in order to make this component configurable and flexible:

  • sidenavTemplateRef: this will provide a placeholder into the component for people to embed other child components into it, using one of the available directives in Angular called NgTemplateOutlet. We’ll come back to it later.
  • duration: sets the duration (in seconds) of the sliding animation of the side nav bar. Default is 0.25 secs.
  • navWidth: sets the width of the side nav bar (in pixels). Default is the width of the window’s innerWidth.
  • direction: this is where we’ll use the enumeration created earlier, which will allow the direction from which the side nav bar will slide out. Default is Left.

Aside from the Inputs, we’re providing a convenient method called onSideBarClose() to close the side nav bar via the service’s setShowNav() and passing the false value.

The method getSideNavBarStyle() gets as an argument the unwrapped value from the Observable showSideNav and builds an object that feeds into Angular’s ngStyle’s directive. We set an object with three properties:

  • transition: which determines the transition for the side nav bar based on the direction and duration provided.
  • width: the side nav bar’s width (either provided or by default the width of the screen).
  • direction: notice how we’re creating the property off of the navBarStyle object on the fly, based on the direction provided.
navBarStyle[this.direction] = 
(showNav ? 0 : (this.navWidth * -1)) + 'px';
...which eventually turns into:navBarStyle.left
...or
navBarStyle.right

Let’s see now how the code maps to the markup in the
side-nav.component.html:

The top container (side-nav-bar) wraps both the overlay (side-nav-bar-overlay) and the main side bar (side-nav-bar-menu-container).

The side-nav-bar applies a class (side-nav-bar-collapsed) upon the showSideNav being true.

The overlay (side-nav-bar-overlay) has a CSS transition animation on its background color and on the visibility that kicks in based on the duration via the ngStyle directive, as well as adding a class (side-nav-bar-overlay-collapsed) upon the showSideNav being true. We want to make the overlay clickable so when the user clicks on the overlay, it also dismisses the side nav bar, hence wiring the onSidebarClose() click event to it as well.

Notice the side-nav-bar-menu-container and how we’re applying the CSS style object we’re building via the getSideNavBarStyle method and providing the unwrapped value of the showSideNav property to this method. The returned object (shown earlier) is fed into the ngStyle directive as well.

Note: Every time one of the properties inside the getSideNavBarStyle gets changed, Angular takes care of re-invoking it, regenerates the CSS ngStyle object and the CSS transitions get kicked off again. The rest is taken care of for you.

We added a close button (side-nav-bar-close) to close the side bar with an icon provided by the Material Icons from Google (CDN added earlier).

And to wrap up the markup for this component we have a container (side-nav-bar-content-container) that wraps up (pun intended) the content that will be displayed inside the side nav bar. The content is a ng-container using the ngTemplateOutlet option. A NgTemplateOutlet is nothing more than a placeholder for you to embed content into it dynamically and programmatically.

Now, the CSS for the side nav bar, in the file side-nav-bar.component.scss:

CSS Tricks Applied (And Explained)

  • The trick with the overlay is that the overlay is always present — by using the CSS style visibility: collapsed we allow it to still remain in place while not occupying any space in the layout and hiding the element entirely — clicking events are still captured by elements underneath the overlay. When the overlay is visible, it receives its click event to hide the side bar, and with a CSS transition we fade not only its background color, but animate the visibility, allowing clicks to go through after it’s collapsed.
  • The trick about making the side-nav-bar stay on top regardless of the page scrolling is using the position: fixed CSS attribute and giving it a high z-index so it stays on top.
  • The trick about making the side-nav-bar-menu-container animatable using CSS transitions is making it position: absolute, so it moves along the coordinates on which the transition will occur — in our case, the left and right coordinates.

This is what the component looks like schematically:

You see how the side-nav-bar sits on the side while the overlay is there; we slide the nav bar in and out with a CSS transition, and we fade the overlay’s background and its visibility on and off as well. The side bar sits on top of the application while all other pages lay beneath.

Now that the component has been created, let’s learn how to apply it in the application.

How do I use this component?

Very simple! Let’s go back to the app.component.html and let’s update this component with the following content:

Notice how we’re adding the side nav bar component created earlier at the very top of the app.component.html via the custom selector app-side-nav, and how the inputs are provided to it:

  • direction: right
  • navWidth: 280
  • duration: 0.5

Now, check out how we are supplying the side nav bar with a reference to a template to be displayed inside its inner placeholder sidenavTemplateRef: by providing the id of an existing template in the markup (ng-template #navContent) — what will happen is that Angular will grab the content of the ng-template by the id navContent (literally anything in here), pass it as an input to the sideNavTemplateRef, and embed it in the ngTemplateOutlet defined inside the side nav bar with the ngTemplateOutlet reference by the same name (sideNavTemplateRef). This is a flexible way to embed content. The side bar has no clue what content has been embedded in its template outlet — all it knows is that it got something that needs to be embedded there.

Now that I added it, how do I see it in action?

Someone needs to trigger it.

Yeah, you’re right. And for that we’ll create a simple component that will represent a header for this app. This way we’ll demonstrate how a totally separate component can trigger the opening of the side bar.

Let’s issue the Angular CLI command to create a new and simple component for the header:

ng generate component header

As always, it generates the three pieces we need (HTML, SCSS and TS files). Let’s start with the Typescript called header.component.ts:

Notice how simple it is. It only injects the NavigationService in its constructor via Angular’s Dependency Injection system, and inside a method called toggleSideNav() just invokes the setShowNav to true. This will trigger a sequence of events that will set the BehaviorSubject showNav$ to true, and whoever is subscribed / listening to this change, will act upon accordingly. That’s the beauty of asynchronous, event-driven programming in action!

The header.component.html looks like this:

It’s just pretty much a simple layout structure, but the thing worth mentioning is that we’re using a span tag as a button, since we attach a click event to it that triggers the toggleSideNav() event and adds a Material Icon that makes it look like a hamburger menu (commonly used for menus).

The SCSS for the header.component.scss is even simpler:

These styles alter the default styles of the Material Icon by increasing the font size, adding some padding and altering the cursor — making this strategy of importing icons into your apps very flexible and powerful!

Update the app.component

Now that we’ve created the header component, now we have someone who will trigger the side nav bar to open! Let’s update the app.component.html file and bring the header in at the right place:

Notice the app-header selector being placed right inside the main-container container, but also right above the section that encloses the router-outlet. We want it to be always on top of every page that gets displayed, that way it is global to the whole application regardless of which page I’m in.

Once all it’s wired up, It will end up looking like this:

BONUS: Adding a Home Page

I created a simple component just to illustrate my point of having pages that sit on top of the header and show through the app-component’s router-outlet. In the src/pages folder I created the page component called home.component, added the configuration required in the app-routing.module.ts so it points to the HomeComponent just created.

After rebuilding, the app will end up looking like this:

EXTRA BONUS: A separate component for the menu content.

Talk about decoupling of components — now I made a separate component that will house the navigational items. I called the component side-nav-content, which contained the links to pages throughout my app using Angular’s Router mechanism. Upon clicking on the links, it would navigate to the corresponding page, while also hiding the side nav bar. The app will look like this after this change:

Clicking on the nav links also closes the side nav bar, but without explicitly calling the NavigationService’s setShowNav() to false

How did I accomplish closing the side nav bar upon a navigation change without explicitly calling the NavigationService’s setShowNav() to false? In the NavigationService’s constructor, I inject Angular’s Router, and I listen for router.events — any navigation action (in my case a navigation change to another page) will trigger inside a subscription I attached to the router.events. See the constructor in the updated version of the NavigationService:

See the full implementation of this tutorial on my Github Repo.

And with that, I conclude this post, wishing this was a helpful tutorial for you on how to leverage the power of Angular and bringing you on a journey with me exploring the following:

  • Creating configurable components using Angular Inputs to increase their flexibility and reusability — we created a simple side nav bar, a header and a navigation links component, communicating seamlessly in a highly decoupled way
  • Simple Templating techniques for component embedding — where using the NgTemplateOutlet we embedded components and content inside other components in a simple yet flexible way.
  • Using services to enable highly decoupled communication between components — using an asynchronous, event-driven programming approach using services to communicate with components, trigger events.e tc.
  • Using CSS in conjunction with Angular directives to create minimal-code animated transitions for your Angular Components — where we used CSS transitions in conjunction with Angular directives (ngStyle, ngIf, ngClass) to create slick and simple animation effects that make your application more polished, using minimal code.

Again, you can see the full implementation of this tutorial on my Github Repo.

Please follow me on Twitter @drcoderz to check up on what I’m into these days, and don’t forget to clap below to show your appreciation — seeing claps somehow motivate me to write, so please do!

Happy Sliding!

--

--

Flutter GDE / GDG Lawrence Lead Organizer / Follow me on Twitter @drcoderz — Subscribe to my YouTube Channel https://tinyurl.com/romanjustcodes