Angular
Angular cheat sheet covering components, services, RxJS, dependency injection, routing, and TypeScript patterns with code examples.
Sign in to mark items as known and track your progress.
Sign inSetup & CLI
Angular installation and CLI commands
Installation & Setup
Setting up Angular development environment
# Install Angular CLI globally
npm install -g @angular/cli
# Create new project
ng new my-app
# Options: routing, CSS/SCSS/Sass/Less
# Start development server
ng serve
ng serve --open # Opens browser
ng serve --port 4201
# Build for production
ng build
ng build --configuration productionAngular CLI Commands
Essential Angular CLI commands for development
# Generate components
ng generate component user
ng g c user --inline-template --inline-style
ng g c components/user-list
# Generate services
ng generate service user
ng g s services/auth
# Generate modules
ng g module admin --routing
ng g module shared
# Generate other artifacts
ng g directive highlight
ng g pipe currency-format
ng g guard auth
ng g interceptor auth
ng g class models/user --type=model
ng g interface models/user
ng g enum models/statusComponents
Angular components and lifecycle
Component Basics
Standalone components are the default in Angular 17+. NgModule is legacy.
// Standalone component (default in Angular 17+)
// Generate: ng generate component user-card --standalone
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-user-card',
standalone: true,
imports: [CommonModule], // Import dependencies directly
template: \`
<div class="card">
<h2>{{ name }}</h2>
<p>{{ email }}</p>
</div>
\`,
styles: [\`.card { padding: 1rem; }\`],
})
export class UserCardComponent {
name = 'Alice';
email = 'alice@example.com';
}
// Bootstrap a standalone app
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent);Component Communication
Input, Output, and component interaction
// Parent to child - @Input
@Component({
selector: 'app-child',
template: '<p>{{ message }}</p>'
})
export class ChildComponent {
@Input() message: string;
@Input() user: User;
}
// Child to parent - @Output
@Component({
selector: 'app-child',
template: '<button (click)="send()">Send</button>'
})
export class ChildComponent {
@Output() messageEvent = new EventEmitter<string>();
send() {
this.messageEvent.emit('Hello Parent');
}
}ViewChild & ContentChild
Accessing child components and elements
// ViewChild - Access child in template
@Component({
selector: 'app-parent',
template: \`
<app-child #childRef></app-child>
<button (click)="callChildMethod()">Call Child</button>
\`
})
export class ParentComponent {
@ViewChild('childRef') child: ChildComponent;
@ViewChild(ChildComponent) childByType: ChildComponent;
callChildMethod() {
this.child.doSomething();
}
}Lifecycle Hooks
Component lifecycle methods
export class MyComponent implements OnInit, OnDestroy {
// 1. Constructor
constructor() { }
// 2. OnChanges
ngOnChanges(changes: SimpleChanges) { }
// 3. OnInit
ngOnInit() { }
// 4. DoCheck
ngDoCheck() { }
// 5. AfterContentInit
ngAfterContentInit() { }
// 6. AfterContentChecked
ngAfterContentChecked() { }
// 7. AfterViewInit
ngAfterViewInit() { }
// 8. AfterViewChecked
ngAfterViewChecked() { }
// 9. OnDestroy
ngOnDestroy() { }
}Templates & Directives
Template syntax and built-in directives
Template Syntax
Data binding and template expressions
<!-- Interpolation -->
<h1>{{ title }}</h1>
<p>{{ 'Hello ' + name }}</p>
<p>{{ getFullName() }}</p>
<!-- Property binding -->
<img [src]="imageUrl">
<button [disabled]="isDisabled">Click</button>
<!-- Event binding -->
<button (click)="onClick()">Click</button>
<input (keyup)="onKeyUp($event)">
<!-- Two-way binding -->
<input [(ngModel)]="name">
<!-- Template reference -->
<input #nameInput>
<button (click)="greet(nameInput.value)">Greet</button>Control Flow (@if / @for / @switch)
Built-in template control flow — replaces *ngIf/*ngFor/*ngSwitch in Angular 17+.
<!-- @if / @else if / @else -->
@if (user) {
<p>Hello, {{ user.name }}</p>
} @else if (loading) {
<p>Loading...</p>
} @else {
<p>Sign in to continue</p>
}
<!-- @for (REQUIRES track) -->
@for (item of items; track item.id) {
<li>{{ item.name }}</li>
} @empty {
<li>No items</li>
}
<!-- @switch -->
@switch (status) {
@case ('loading') { <p>Loading...</p> }
@case ('error') { <p>Error!</p> }
@default { <p>Ready</p> }
}Deferrable Views (@defer)
Lazy-load template chunks with @defer blocks (Angular 17+).
<!-- Basic @defer with @loading / @error / @placeholder -->
@defer {
<heavy-chart [data]="data" />
} @loading {
<p>Loading chart...</p>
} @placeholder {
<p>Chart will appear here</p>
} @error {
<p>Failed to load</p>
}
<!-- @defer with triggers -->
@defer (on viewport) {
<comments-section />
}
@defer (on idle) {
<analytics-widget />
}
@defer (on interaction) {
<chat-widget />
}Services & DI
Services and dependency injection
Services
Creating and using Angular services
// Service with @Injectable
@Injectable({
providedIn: 'root'
})
export class UserService {
constructor(private http: HttpClient) {}
getUsers(): Observable<User[]> {
return this.http.get<User[]>('/api/users');
}
getUser(id: number): Observable<User> {
return this.http.get<User>(\`/api/users/\${id}\`);
}
}
// Using in component
constructor(private userService: UserService) {}
ngOnInit() {
this.userService.getUsers().subscribe(users => {
this.users = users;
});
}Dependency Injection
DI patterns and providers
// Injection token
const API_URL = new InjectionToken<string>('api.url');
// Provider configuration
providers: [
UserService,
{ provide: API_URL, useValue: 'https://api.example.com' },
{ provide: Logger, useClass: ConsoleLogger },
{ provide: Storage, useFactory: storageFactory, deps: [Window] }
]
// Inject in component
constructor(
private userService: UserService,
@Inject(API_URL) private apiUrl: string,
@Optional() private logger?: Logger
) {}Routing
Angular router configuration and navigation
Route Configuration
Setting up routes and navigation
// Basic routes
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'users/:id', component: UserComponent },
{ path: '**', component: NotFoundComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
// Router outlet in template
<router-outlet></router-outlet>
// Router links
<a routerLink="/about">About</a>
<a [routerLink]="['/users', userId]">User Profile</a>Guards & Resolvers
Route guards and data resolvers
// CanActivate guard
@Injectable()
export class AuthGuard implements CanActivate {
constructor(
private auth: AuthService,
private router: Router
) {}
canActivate(): boolean {
if (this.auth.isAuthenticated()) {
return true;
}
this.router.navigate(['/login']);
return false;
}
}
// Resolver
@Injectable()
export class UserResolver implements Resolve<User> {
constructor(private userService: UserService) {}
resolve(route: ActivatedRouteSnapshot): Observable<User> {
return this.userService.getUser(route.params['id']);
}
}Forms
Template-driven and reactive forms
Template-Driven Forms
Forms with ngModel and template validation
<!-- Basic form -->
<form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">
<input name="name" [(ngModel)]="user.name" required>
<input name="email" [(ngModel)]="user.email" email>
<button type="submit" [disabled]="!myForm.valid">
Submit
</button>
</form>
// Component
onSubmit(form: NgForm) {
if (form.valid) {
console.log(form.value);
}
}Reactive Forms
Forms with FormControl and validators
// Form setup
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
export class MyComponent {
myForm: FormGroup;
constructor(private fb: FormBuilder) {
this.myForm = this.fb.group({
name: ['', [Validators.required, Validators.minLength(3)]],
email: ['', [Validators.required, Validators.email]],
age: [null, [Validators.min(18)]]
});
}
onSubmit() {
if (this.myForm.valid) {
console.log(this.myForm.value);
}
}
}HTTP & Observables
HTTP client and RxJS observables
HTTP Client
Making HTTP requests with HttpClient
// Import HttpClientModule in app.module.ts
import { HttpClientModule } from '@angular/common/http';
// Service with HTTP
@Injectable()
export class DataService {
constructor(private http: HttpClient) {}
getData(): Observable<Data[]> {
return this.http.get<Data[]>('/api/data');
}
postData(data: Data): Observable<Data> {
return this.http.post<Data>('/api/data', data);
}
}RxJS Observables
Working with observables and operators
// Creating observables
const obs$ = new Observable(observer => {
observer.next('Hello');
observer.next('World');
observer.complete();
});
// Subscribe
const subscription = obs$.subscribe({
next: value => console.log(value),
error: err => console.error(err),
complete: () => console.log('Complete')
});
// Unsubscribe
subscription.unsubscribe();Advanced Features
Pipes, directives, and performance
Pipes & Directives
Custom pipes and directives
// Custom pipe
@Pipe({ name: 'truncate' })
export class TruncatePipe implements PipeTransform {
transform(value: string, limit = 100): string {
return value.length > limit
? value.substring(0, limit) + '...'
: value;
}
}
// Usage
{{ longText | truncate:50 }}
// Custom directive
@Directive({ selector: '[appHighlight]' })
export class HighlightDirective {
@Input() appHighlight = 'yellow';
@HostListener('mouseenter') onMouseEnter() {
this.el.nativeElement.style.backgroundColor = this.appHighlight;
}
constructor(private el: ElementRef) {}
}Signals (Angular 16+)
Reactive primitive for state management
Signal Basics
Creating and using signals for reactive state
// Creating signals
import { signal, computed, effect } from '@angular/core';
@Component({
selector: 'app-counter',
template: \`
<p>Count: {{ count() }}</p>
<p>Double: {{ doubleCount() }}</p>
<button (click)="increment()">+</button>
\`
})
export class CounterComponent {
// Writable signal
count = signal(0);
// Computed signal (read-only)
doubleCount = computed(() => this.count() * 2);
increment() {
this.count.set(this.count() + 1);
// or: this.count.update(v => v + 1);
}
}Signal Outputs, linkedSignal & Resources
Signal-based outputs and async resources (Angular 17.3+, 19+).
import { output, linkedSignal, resource } from '@angular/core';
// === Signal-based output (17.3+) ===
@Component({ /* ... */ })
export class ToggleComponent {
// Replaces @Output() EventEmitter
toggled = output<boolean>();
handleClick() {
this.toggled.emit(true);
}
}
// === linkedSignal (19+) ===
// A writable derived signal — auto-syncs with source but can be overridden
@Component({ /* ... */ })
export class ShippingForm {
country = signal('US');
// Resets when country changes, but user can override
state = linkedSignal(() => this.defaultStateFor(this.country()));
}
// === resource() (19+) — async signal ===
@Component({ /* ... */ })
export class UserDetail {
userId = signal(1);
user = resource({
request: () => ({ id: this.userId() }),
loader: async ({ request }) => {
const res = await fetch(\`/api/users/\${request.id}\`);
return res.json();
},
});
}
// In template:
// @if (user.isLoading()) { <p>Loading...</p> }
// @if (user.error()) { <p>Error: {{ user.error() }}</p> }
// @if (user.value(); as u) { <p>{{ u.name }}</p> }Animations
Built-in Angular animation API
Animation Basics
Creating animations with Angular animations API
// Basic animation
import { trigger, state, style, transition, animate } from '@angular/animations';
@Component({
selector: 'app-animated',
template: \`
<div [@slideIn]="isVisible" class="box">
Animated Content
</div>
<button (click)="toggle()">Toggle</button>
\`,
animations: [
trigger('slideIn', [
transition(':enter', [
style({ transform: 'translateX(-100%)' }),
animate('300ms ease-in', style({ transform: 'translateX(0%)' }))
]),
transition(':leave', [
animate('300ms ease-out', style({ transform: 'translateX(100%)' }))
])
])
]
})
export class AnimatedComponent {
isVisible = true;
toggle() { this.isVisible = !this.isVisible; }
}Testing
Built-in testing with Karma, Jasmine, and TestBed
Component Testing
Testing Angular components with TestBed
// Basic component test
import { ComponentFixture, TestBed } from '@angular/core/testing';
describe('UserComponent', () => {
let component: UserComponent;
let fixture: ComponentFixture<UserComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ UserComponent ]
}).compileComponents();
fixture = TestBed.createComponent(UserComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should render title', () => {
const compiled = fixture.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('User Profile');
});
});Performance
Built-in performance optimization features
Performance Optimization
OnPush strategy, trackBy, and lazy loading
// OnPush Change Detection
@Component({
selector: 'app-optimized',
changeDetection: ChangeDetectionStrategy.OnPush,
template: \`
<div *ngFor="let item of items; trackBy: trackByFn">
{{ item.name }}
</div>
\`
})
export class OptimizedComponent {
@Input() items: Item[];
trackByFn(index: number, item: Item): number {
return item.id; // Return unique identifier
}
}
// Lazy Loading Routes
const routes: Routes = [
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
}
];Security
Built-in security features and sanitization
Security & Sanitization
XSS protection and content sanitization
// Angular automatically sanitizes values
@Component({
template: \`
<!-- Automatically sanitized -->
<div [innerHTML]="userContent"></div>
<!-- Text interpolation is always safe -->
<p>{{ userInput }}</p>
\`
})
export class SafeComponent {
userContent = '<script>alert("XSS")</script>'; // Script tags removed
userInput = '<img src=x onerror="alert(1)">'; // Displayed as text
}Accessibility
Built-in accessibility features and ARIA support
Accessibility (A11y)
ARIA attributes and CDK A11y utilities
// Basic ARIA attributes
@Component({
template: \`
<button [attr.aria-label]="buttonLabel"
[attr.aria-pressed]="isPressed"
[attr.aria-disabled]="isDisabled">
{{ buttonText }}
</button>
<div role="alert" aria-live="polite">
{{ statusMessage }}
</div>
\`
})
export class AccessibleComponent {
buttonLabel = 'Submit form';
isPressed = false;
isDisabled = false;
statusMessage = '';
}i18n
Built-in internationalization support
Internationalization (i18n)
Built-in i18n for multi-language support
// Mark text for translation
@Component({
template: \`
<p i18n="@@welcome.message">Welcome to our app!</p>
<p i18n="User greeting|Greeting message for user@@user.greeting">
Hello, {{ userName }}!
</p>
<button i18n="@@button.submit">Submit</button>
\`
})
// Extract messages for translation
ng extract-i18n
ng extract-i18n --output-path src/locale