Angular Development Coding Conventions
Angular Development Coding Conventions
1. Project Structure
Top-Level Directories
src/: Houses all source code.src/app/: Contains the main app module and core components.src/assets/: Stores static resources (e.g., images, fonts).src/environments/: Holds environment-specific configurations.
Feature Modules
- Organize each feature into a dedicated folder.
- Leverage feature modules to partition the app for better scalability.
src/
├── app/
│ ├── core/ # Singleton services, global components (e.g., navbar)
│ ├── shared/ # Reusable components, directives, pipes
│ ├── dashboard/ # Feature module: Dashboard
│ ├── settings/ # Feature module: Settings
│ └── app.module.ts # Root module
├── assets/
├── environments/
└── main.ts
Module Substructure
Each feature module should include subfolders for components, services, models, etc.
dashboard/
├── components/
│ ├── stats-card/
│ │ ├── stats-card.component.ts
│ │ ├── stats-card.component.html
│ │ ├── stats-card.component.scss
│ │ └── stats-card.component.spec.ts
│ └── chart-widget/
├── services/
│ └── dashboard-data.service.ts
├── models/
│ └── dashboard-metric.model.ts
├── pipes/
│ └── number-format.pipe.ts
└── directives/
└── hover-highlight.directive.ts
2. Naming Conventions
File Naming
- Use kebab-case for filenames.
- Example:
user-dashboard.component.ts,data-fetcher.service.ts.
Classes and Interfaces
- Use PascalCase for class and interface names.
- Example:
UserDashboardComponent,DataFetcherService.
Variables and Functions
- Use camelCase for variable and function names.
- Example:
userName,fetchUserProfile().
Component Selectors
- Use kebab-case with an app prefix for component selectors.
- Example:
app-user-dashboard.
Enums
- Enum names use PascalCase; enum values use UPPER_SNAKE_CASE.
export enum UserRole {
ADMIN = 'ADMIN',
EDITOR = 'EDITOR'
}
3. Code Style
Quotes
- Prefer single quotes (
'); use double quotes (") only when a string contains single quotes. - Example:
const message = 'Hello, "Angular"!';
Indentation and Whitespace
- Use 2 spaces for indentation (avoid tabs).
- Add a single space after commas, colons, and semicolons in style/configuration files.
Semicolons
- Terminate all statements with a semicolon.
Arrow Functions
- Use arrow functions to preserve
thiscontext (e.g., in callbacks).
const users = [1, 2, 3].map(id => this.getUser(id));
Template Strings
- Use backticks (
`) for multi-line or dynamic strings.
const html = `<div class="user-card">${user.name}</div>`;
Comments
- Add comments for complex logic or critical business rules (avoid redundant comments).
// Check if user has admin permissions (role ID: 100)
if (user.roleId === 100) {
// ...
}
Empty Lines
- Insert empty lines between code blocks (e.g., imports, class methods, logical sections) to improve readability.
Line Length
- Limit lines to 120 characters; break long lines for clarity.
4. Components & Templates
Component Classes
- Each component should have a single responsibility.
- Use the
OnInitlifecycle hook for initialization logic.
import { Component, OnInit } from '@angular/core';
import { UserService } from '../services/user.service';
import { User } from '../models/user.model';
@Component({
selector: 'app-user-details',
templateUrl: './user-details.component.html',
styleUrls: ['./user-details.component.scss']
})
export class UserDetailsComponent implements OnInit {
currentUser: User | null = null;
constructor(private userService: UserService) {}
ngOnInit(): void {
this.userService.fetchUser().subscribe(user => {
this.currentUser = user;
});
}
}
Template Files
- Use either inline templates (
template) or external files (templateUrl). - Keep template expressions simple; move complex logic to component methods.
<div *ngIf="currentUser">
<h2>{{ currentUser.name }}</h2>
<p>{{ currentUser.email }}</p>
</div>
Styles
- Use SCSS for styling; scope styles to the component using
:hostor component-specific classes.
.user-details {
h2 {
color: #2c3e50;
}
}
Data Binding
- Prefer one-way binding (
[property]) for most cases; use two-way binding ([(ngModel)]) only when necessary.
<input [value]="user.name" (input)="updateUserName($event)">
Event Binding
- Use
(event)syntax for event handlers.
<button (click)="submitForm()">Submit</button>
Directives & Pipes
- Define custom directives/pipes in a shared module (export them for reuse).
- Leverage built-in directives (e.g.,
*ngIf,*ngFor) and pipes (e.g.,| date).
@Pipe({ name: 'titleCase' })
export class TitleCasePipe implements PipeTransform {
transform(value: string): string {
return value.split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
}
}
5. Services & Dependency Injection
Service Classes
- Mark services with
@Injectableand specifyprovidedInto manage scope.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from '../models/user.model';
@Injectable({
providedIn: 'root' // Provides a singleton instance
})
export class UserService {
constructor(private http: HttpClient) {}
fetchUser(): Observable<User> {
return this.http.get<User>('/api/user');
}
}
Dependency Injection
- Inject services via the constructor (Angular’s DI system handles instantiation).
export class UserDetailsComponent {
constructor(private userService: UserService) {}
}
Provider Scope
- Provide services at the module level (via
@NgModule.providers) or component level (via@Component.providers).
6. Modules
NgModule Structure
- Each module (root, feature, shared) should be a dedicated
@NgModule. - Limit the number of declarations/imports per module for maintainability.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UserDetailsComponent } from './components/user-details/user-details.component';
import { UserListComponent } from './components/user-list/user-list.component';
import { UserService } from './services/user.service';
@NgModule({
declarations: [UserDetailsComponent, UserListComponent],
imports: [CommonModule],
providers: [UserService],
exports: [UserDetailsComponent]
})
export class UserModule { }
Shared Module
- A shared module declares and exports reusable components, directives, and pipes.
@NgModule({
declarations: [CommonButtonComponent, HighlightDirective, TitleCasePipe],
exports: [CommonButtonComponent, HighlightDirective, TitleCasePipe],
imports: [CommonModule]
})
export class SharedModule { }
Core Module
- The core module contains singleton services (e.g., authentication, API) and global components.
@NgModule({
providers: [AuthService, ApiService],
declarations: [NavbarComponent],
exports: [NavbarComponent]
})
export class CoreModule { }
7. Routing
Route Configuration
- Define routes in a dedicated module (e.g.,
AppRoutingModule). - Use lazy loading for feature modules to optimize initial load time.
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: 'dashboard',
loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule)
},
{
path: 'settings',
loadChildren: () => import('./settings/settings.module').then(m => m.SettingsModule)
},
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
8. Testing
- Write unit tests for components, services, and pipes using Angular testing utilities (e.g.,
TestBed,ComponentFixture). - Place test files alongside the component/service (e.g.,
user-details.component.spec.ts).
9. Documentation & Comments
- Document public APIs (components, services) using JSDoc-style comments.
- Add inline comments for complex algorithms or non-obvious logic.
10. Version Control
- Add a
.gitignorefile to exclude build artifacts (e.g.,dist/,node_modules/). - Commit frequently with descriptive messages (e.g., "feat: Add user dashboard component").