Creating an expandable and collapsible sidebar in Angular can significantly enhance the user experience of your application. This tutorial provides a step-by-step guide to building such a sidebar, complete with icons and smooth transitions. We’ll cover everything from setting up the component structure to applying styles and logic for toggling the sidebar.
A collapsible sidebar improves the usability of an application by:
Saving screen space.
Providing easy navigation.
Keeping the interface clean and organized.
First, ensure you have Angular CLI installed. If not, run:
npm install -g @angular/cli
Create a new Angular project:
ng new angular-sidebar
cd angular-sidebar
Generate the necessary components:
ng generate component sidebar
app.component.html
This will serve as the main container for the application. Add the sidebar and a button for toggling its state:
<div class="app-container">
<div class="position-relative">
<app-sidebar
[isSidebarCollapsed]="isSidebarCollapsed"
(sidebarToggle)="onSidebarToggle()"
>
</app-sidebar>
<button
class="sidebar-toggle-btn"
[ngClass]="{ 'sidebar-collapsed': isSidebarCollapsed }"
(click)="onSidebarToggle()"
>
<i
[class]="
isSidebarCollapsed ? 'fas fa-arrow-right' : 'fas fa-arrow-left'
"
></i>
</button>
</div>
<main class="content" [ngClass]="{ 'content-expanded': isSidebarCollapsed }">
<div class="content-inner">Content goes here</div>
</main>
</div>
app.component.ts
Add the logic to manage the sidebar's state:
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
isSidebarCollapsed = false;
onSidebarToggle() {
this.isSidebarCollapsed = !this.isSidebarCollapsed;
}
}
sidebar.component.html
Define the sidebar's HTML structure, including a menu with nested items:
<div class="sidebar" [ngClass]="{ 'sidebar-collapsed': isSidebarCollapsed }">
<div class="sidebar-header">
<a href="#" class="sidebar-logo">{{
isSidebarCollapsed ? 'C' : 'CodeWithChintan'
}}</a>
</div>
<div class="sidebar-menu">
<ul>
<ng-container *ngFor="let item of menuItems">
<li class="sidebar-menu-item">
<a
href="#"
class="sidebar-item"
[ngClass]="{
'has-children': item.children,
'menu-item-active': item.isOpen
}"
(click)="toggleMenuItem(item)"
>
<i [class]="item.icon"></i>
<span class="sidebar-item-text">{{ item.label }}</span>
<i
*ngIf="item.children && !isSidebarCollapsed"
class="fas fa-chevron-down sidebar-item-arrow"
[ngClass]="{ rotated: item.isOpen }"
></i>
</a>
<ul
*ngIf="item.children && !isSidebarCollapsed && item.isOpen"
class="sidebar-submenu"
>
<li *ngFor="let child of item.children">
<a href="#" class="sidebar-item sidebar-subitem">
<i [class]="child.icon"></i>
<span class="sidebar-item-text">{{ child.label }}</span>
</a>
</li>
</ul>
</li>
</ng-container>
</ul>
</div>
</div>
sidebar.component.ts
Handle the toggle logic for menu items and sidebar:
import { Component, EventEmitter, Input, Output } from '@angular/core';
interface MenuItem {
icon: string;
label: string;
children?: MenuItem[];
isOpen?: boolean;
}
@Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.scss'],
})
export class SidebarComponent {
@Input() isSidebarCollapsed = false;
@Output() sidebarToggle = new EventEmitter<void>();
menuItems: MenuItem[] = [
{
icon: 'fas fa-home',
label: 'Dashboard',
isOpen: false,
children: [
{ icon: 'fas fa-chart-pie', label: 'Analytics' },
{ icon: 'fas fa-tasks', label: 'Projects' },
]
},
{
icon: 'fas fa-cog',
label: 'Settings',
isOpen: false,
children: [
{ icon: 'fas fa-user', label: 'Profile' },
{ icon: 'fas fa-lock', label: 'Security' },
]
},
{
icon: 'fas fa-envelope',
label: 'Messages'
}
];
toggleSidebar() {
this.sidebarToggle.emit();
}
toggleMenuItem(item: MenuItem) {
// Only toggle if sidebar is not collapsed and item has children
if (!this.isSidebarCollapsed && item.children) {
item.isOpen = !item.isOpen;
}
}
}
app.component.scss
Add global styles for layout and transitions:
.app-container {
display: flex;
height: 100vh;
overflow: hidden;
}
.content {
flex-grow: 1;
margin-left: 250px;
transition: all 0.3s ease-in-out;
background-color: #f4f6f7;
overflow-y: auto;
&-inner {
padding: 2rem;
max-width: 1200px;
margin: 0 auto;
}
&-expanded {
margin-left: 50px;
}
}
.sidebar-toggle-btn {
position: absolute;
top: 1rem;
left: 200px; // Default position when sidebar is expanded
background-color: #2c3e50;
border: none;
color: #fff;
padding: 0.5rem;
border-top-right-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
cursor: pointer;
z-index: 1001;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
&:hover {
background-color: #34495e;
}
&.sidebar-collapsed {
left: 15px; // Position when sidebar is collapsed
}
}
sidebar.component.scss
Define styles for the sidebar and menu:
.sidebar {
background-color: #2c3e50;
color: #ecf0f1;
height: 100vh;
width: 250px;
position: fixed;
top: 0;
left: 0;
z-index: 1000;
transition: all 0.3s ease-in-out;
overflow-x: hidden;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
}
.sidebar-header {
display: flex;
justify-content: center;
align-items: center;
padding: 1.5rem;
position: relative;
}
.sidebar-logo {
color: #fff;
text-decoration: none;
font-size: 1.5rem;
font-weight: bold;
text-align: center;
}
.sidebar-menu {
padding: 1rem 0;
ul {
list-style-type: none;
padding: 0;
margin: 0;
}
}
.sidebar-menu-item {
position: relative;
}
.sidebar-item {
display: flex;
align-items: center;
color: #ecf0f1;
text-decoration: none;
padding: 0.75rem 1rem;
transition: all 0.2s ease;
cursor: pointer;
&:hover {
background-color: rgba(255, 255, 255, 0.1);
}
&.menu-item-active {
background-color: rgba(255, 255, 255, 0.2);
}
i {
margin-right: 0.75rem;
&.sidebar-item-arrow {
margin-left: auto;
font-size: 0.8rem;
transition: transform 0.3s ease;
&.rotated {
transform: rotate(180deg);
}
}
}
&-text {
opacity: 1;
transition: opacity 0.3s ease-in-out;
}
&.has-children {
position: relative;
}
}
.sidebar-submenu {
background-color: rgba(0, 0, 0, 0.1);
.sidebar-item {
padding-left: 3rem;
font-size: 0.9rem;
}
}
.sidebar-collapsed {
width: 50px;
.sidebar-menu-item {
position: static;
}
.sidebar-item {
i {
margin-right: 0;
}
&-text,
&-arrow {
opacity: 0;
width: 0;
overflow: hidden;
}
}
.sidebar-submenu {
display: none;
}
}
Start the development server:
ng serve
Navigate to http://localhost:4200/
to see your sidebar in action.
Modify the menuItems
array in sidebar.component.ts
and provide appropriate icon classes.
Yes, use Angular’s animation module to add smooth transitions when menus open and close.
Update the width
property in sidebar.component.scss
for the expanded and collapsed states.
This guide covers all the essential steps to create a functional expandable and collapsible sidebar in Angular. You can further customize the design and functionality to meet your application needs.