Angular Fundamentals
This guide introduces core Angular concepts using examples from the TimeLock project. If you're new to Angular, this is your starting point.
Table of Contents
- What is Angular?
 - Components
 - Services and Dependency Injection
 - Signals (Reactive State Management)
 - Routing
 - Modules vs Standalone Components
 - TypeScript Integration
 
What is Angular?
Angular is a TypeScript-based web application framework developed by Google. It provides:
- Component-based architecture - Build encapsulated components that manage their own state
 - Dependency injection - Manage dependencies efficiently
 - Reactive programming - Handle asynchronous data with RxJS and Signals
 - Routing - Navigate between different views
 - Forms - Handle user input with powerful form controls
 - HTTP client - Communicate with backend services
 
Key Angular Concepts in TimeLock
TimeLock uses Angular 20 with modern patterns:
- Standalone components (no NgModules needed)
 - Signals for reactive state management
 - Lazy loading for performance
 - Dependency injection for services
 
Components
Components are the building blocks of Angular applications. Each component consists of:
Component Structure
@Component({
  selector: 'app-example',           // HTML tag name
  standalone: true,                 // Modern standalone component
  imports: [CommonModule],          // Dependencies
  template: `<h1>{{title}}</h1>`,  // HTML template
  styleUrls: ['./example.css']      // Component styles
})
export class ExampleComponent {
  title = 'Hello World';            // Component properties
}
Real Example from TimeLock
// From src/app/components/todo-item/todo-item.component.ts
@Component({
  selector: 'app-todo-item',
  standalone: true,
  imports: [CommonModule, FormsModule, TaskEditorComponent],
  templateUrl: './todo-item.component.html',
  styleUrls: ['./todo-item.component.css']
})
export class TodoItemComponent {
  @Input() todo!: Todo;                    // Input from parent
  @Output() todoUpdated = new EventEmitter<Todo>();  // Output to parent
  
  isEditing = signal(false);               // Component state with signals
  
  toggleComplete() {
    // Component method
    this.todoUpdated.emit({
      ...this.todo,
      completed: !this.todo.completed
    });
  }
}
Component Communication
Parent to Child: Use @Input()
// Parent template
<app-todo-item [todo]="selectedTodo"></app-todo-item>
// Child component
@Input() todo!: Todo;
Child to Parent: Use @Output() and EventEmitter
// Child component
@Output() todoUpdated = new EventEmitter<Todo>();
onUpdate() {
  this.todoUpdated.emit(this.todo);
}
// Parent template
<app-todo-item (todoUpdated)="handleTodoUpdate($event)"></app-todo-item>
Services and Dependency Injection
Services handle business logic and data management. Angular's dependency injection system provides services to components.
Creating a Service
@Injectable({
  providedIn: 'root'  // Available application-wide
})
export class TodoService {
  private todos = signal<Todo[]>([]);
  
  addTodo(todo: Todo) {
    this.todos.update(todos => [...todos, todo]);
  }
  
  getTodos() {
    return this.todos.asReadonly();
  }
}
Using Services in Components
export class TodoListComponent {
  private todoService = inject(TodoService);  // Modern injection syntax
  
  // Or using constructor injection (traditional way)
  constructor(private todoService: TodoService) {}
  
  todos = this.todoService.getTodos();  // Access service data
}
TimeLock Service Examples
ProjectService - Manages projects
// Simplified example from src/app/services/project.service.ts
@Injectable({ providedIn: 'root' })
export class ProjectService {
  private projectsSignal = signal<Project[]>([]);
  
  projects = this.projectsSignal.asReadonly();
  
  async addProject(projectData: Omit<Project, 'id'>): Promise<string> {
    const newProject = { ...projectData, id: this.generateId() };
    this.projectsSignal.update(projects => [...projects, newProject]);
    await this.saveProjects();
    return newProject.id;
  }
}
Signals
Signals are Angular's modern reactive state management solution (introduced in Angular 16+).
Basic Signals
import { signal, computed } from '@angular/core';
export class ExampleComponent {
  // Writable signal
  count = signal(0);
  
  // Computed signal (derived state)
  doubleCount = computed(() => this.count() * 2);
  
  // Read signal value
  getCurrentCount() {
    return this.count();  // Call like a function
  }
  
  // Update signal
  increment() {
    this.count.update(value => value + 1);
    // Or set directly: this.count.set(5);
  }
}
Signals in Templates
<!-- Display signal values -->
<p>Count: {{count()}}</p>
<p>Double: {{doubleCount()}}</p>
<!-- Use in event handlers -->
<button (click)="increment()">Increment</button>
TimeLock Signal Examples
// From TodoService
private todosSignal = signal<Todo[]>([]);
private filterSignal = signal<FilterType>('all');
// Computed signals for filtered data
activeTodos = computed(() => 
  this.todosSignal().filter(todo => !todo.completed && !todo.archived)
);
completedTodos = computed(() => 
  this.todosSignal().filter(todo => todo.completed)
);
filteredTodos = computed(() => {
  const todos = this.todosSignal();
  const filter = this.filterSignal();
  
  switch (filter) {
    case 'active': return todos.filter(t => !t.completed);
    case 'completed': return todos.filter(t => t.completed);
    default: return todos;
  }
});
Routing
Angular Router enables navigation between different views/pages.
Route Configuration
// From src/app/app.routes.ts
export const routes: Routes = [
  { path: '', redirectTo: '/projects', pathMatch: 'full' },
  { 
    path: 'projects', 
    loadComponent: () => import('./pages/projects/projects.component')
      .then(m => m.ProjectsComponent)
  },
  { 
    path: 'project/:id', 
    loadComponent: () => import('./pages/project-detail/project-detail.component')
      .then(m => m.ProjectDetailComponent)
  }
];
Navigation
// In component
import { Router } from '@angular/router';
export class ExampleComponent {
  private router = inject(Router);
  
  navigateToProject(projectId: string) {
    this.router.navigate(['/project', projectId]);
  }
}
<!-- In template -->
<a routerLink="/projects">Projects</a>
<a [routerLink]="['/project', project.id]">{{project.name}}</a>
Route Parameters
// In component
import { ActivatedRoute } from '@angular/router';
export class ProjectDetailComponent {
  private route = inject(ActivatedRoute);
  
  ngOnInit() {
    const projectId = this.route.snapshot.paramMap.get('id');
    // Or reactive: this.route.paramMap.subscribe(params => ...)
  }
}
Modules vs Standalone Components
TimeLock uses standalone components (Angular 14+), which is the modern approach.
Traditional NgModule Approach (Old)
@NgModule({
  declarations: [AppComponent, TodoComponent],
  imports: [BrowserModule, FormsModule],
  providers: [TodoService],
  bootstrap: [AppComponent]
})
export class AppModule { }
Standalone Components (Modern - Used in TimeLock)
@Component({
  selector: 'app-todo',
  standalone: true,                    // No module needed!
  imports: [CommonModule, FormsModule], // Direct imports
  template: `...`
})
export class TodoComponent { }
// Bootstrap directly
bootstrapApplication(App, {
  providers: [
    provideRouter(routes),
    // other providers
  ]
});
Benefits of Standalone Components
- Simpler - No need to manage NgModules
 - Better tree-shaking - Unused code is eliminated
 - Lazy loading - Components can be loaded on-demand
 - Easier testing - Less boilerplate
 
TypeScript Integration
Angular is built with TypeScript, providing strong typing and better developer experience.
Interface Definitions
// From src/app/models/todo.model.ts
export interface Todo {
  id: string;
  title: string;
  description?: string;        // Optional property
  completed: boolean;
  priority: 'low' | 'medium' | 'high';  // Union types
  dueDate?: Date;
  tags?: string[];
}
Type Safety in Components
export class TodoComponent {
  @Input() todo!: Todo;  // Strongly typed input
  
  updateTodo(updates: Partial<Todo>) {  // Partial type utility
    // TypeScript ensures type safety
    this.todo = { ...this.todo, ...updates };
  }
}
Generic Types
// Generic service method
async loadData<T>(key: string): Promise<T[]> {
  // Implementation
}
// Usage
const todos = await this.loadData<Todo>('todos');
const projects = await this.loadData<Project>('projects');
Next Steps
Now that you understand Angular fundamentals, continue with:
- Project Architecture - See how these concepts work together in TimeLock
 - Getting Started - Set up your development environment
 - Code Structure - Explore the actual codebase