Angular Cheat Sheet

Quick reference guide with essential concepts, syntax or commands

Summary

Angular is a TypeScript-based web application framework developed by Google that uses a component-based architecture with declarative templates, dependency injection, and reactive programming patterns. This cheatsheet covers essential Angular concepts, patterns, and best practices for building scalable web applications and succeeding in technical interviews.

Angular Basics

What is Angular?

  • TypeScript-based open-source framework by Google
  • Component-based architecture
  • Uses RxJS for reactive programming
  • Built-in features: routing, forms, HTTP client, testing

Angular CLI Commands

ng new app-name          # Create new project
ng serve                 # Run dev server
ng generate component    # Generate component
ng build --configuration production  # Production build
ng test                 # Run unit tests
ng e2e                  # Run e2e tests

Components

Basic Component Structure

@Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.css']
})
export class ExampleComponent {
  title = 'Hello Angular';
}

Component Communication

Input (Parent to Child)

// Child
@Input() message: string;

// Parent template
<app-child [message]="parentMessage"></app-child>

Output (Child to Parent)

// Child
@Output() notify = new EventEmitter<string>();
onClick() { this.notify.emit('data'); }

// Parent template
<app-child (notify)="handleNotify($event)"></app-child>

ViewChild

@ViewChild(ChildComponent) child: ChildComponent;
ngAfterViewInit() {
  console.log(this.child.someProperty);
}

Templates & Data Binding

Interpolation

<h1>{{ title }}</h1>
<p>{{ 'Hello ' + name }}</p>

Property Binding

<img [src]="imageUrl">
<button [disabled]="isDisabled">Click</button>

Event Binding

<button (click)="onClick()">Click me</button>
<input (keyup)="onKey($event)">

Two-way Binding

<input [(ngModel)]="name">

Template Reference Variables

<input #phone placeholder="phone">
<button (click)="callPhone(phone.value)">Call</button>

Directives

Structural Directives

ngIf

<div *ngIf="isVisible">Visible content</div>
<div *ngIf="user; else loading">{{ user.name }}</div>
<ng-template #loading>Loading...</ng-template>

ngFor

<li *ngFor="let item of items; let i = index">
  {{ i }}: {{ item }}
</li>

ngSwitch

<div [ngSwitch]="value">
  <p *ngSwitchCase="1">Case 1</p>
  <p *ngSwitchCase="2">Case 2</p>
  <p *ngSwitchDefault>Default</p>
</div>

Attribute Directives

ngClass

<div [ngClass]="{'active': isActive, 'disabled': isDisabled}"></div>
<div [ngClass]="currentClasses"></div>

ngStyle

<div [ngStyle]="{'color': color, 'font-size': size + 'px'}"></div>

Custom Directive

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  @HostListener('mouseenter') onMouseEnter() {
    this.highlight('yellow');
  }
  
  constructor(private el: ElementRef) {}
  
  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}

Services & Dependency Injection

Basic Service

@Injectable({
  providedIn: 'root'  // Singleton service
})
export class DataService {
  getData() {
    return ['data1', 'data2'];
  }
}

Using Service in Component

constructor(private dataService: DataService) {}

ngOnInit() {
  this.data = this.dataService.getData();
}

Injection Tokens

// Token definition
export const API_URL = new InjectionToken<string>('api.url');

// Provider
providers: [
  { provide: API_URL, useValue: 'https://api.example.com' }
]

// Usage
constructor(@Inject(API_URL) private apiUrl: string) {}

Routing

Basic Route Configuration

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'about', component: AboutComponent },
  { path: 'user/:id', component: UserComponent },
  { path: '**', component: NotFoundComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})

Router Outlet & Links

<router-outlet></router-outlet>
<a routerLink="/about" routerLinkActive="active">About</a>

Programmatic Navigation

constructor(private router: Router) {}

navigate() {
  this.router.navigate(['/user', userId]);
  // With query params
  this.router.navigate(['/search'], { queryParams: { q: 'angular' } });
}

Route Guards

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(): boolean {
    return this.authService.isAuthenticated();
  }
}

// Route config
{ path: 'admin', component: AdminComponent, canActivate: [AuthGuard] }

Lazy Loading

{
  path: 'feature',
  loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule)
}

Forms

Template-Driven Forms

<form #f="ngForm" (ngSubmit)="onSubmit(f)">
  <input name="email" ngModel required email>
  <span *ngIf="f.controls.email?.invalid && f.controls.email?.touched">
    Email is invalid
  </span>
  <button [disabled]="f.invalid">Submit</button>
</form>

Reactive Forms

// Component
form = this.fb.group({
  email: ['', [Validators.required, Validators.email]],
  password: ['', [Validators.required, Validators.minLength(8)]]
});

constructor(private fb: FormBuilder) {}

onSubmit() {
  if (this.form.valid) {
    console.log(this.form.value);
  }
}

// Template
<form [formGroup]="form" (ngSubmit)="onSubmit()">
  <input formControlName="email">
  <div *ngIf="form.get('email')?.invalid && form.get('email')?.touched">
    Email is required
  </div>
</form>

Custom Validator

function emailValidator(control: AbstractControl): ValidationErrors | null {
  const email = control.value;
  if (!email || (email.includes('@') && email.includes('.'))) {
    return null;
  }
  return { invalidEmail: true };
}

// Usage
form = this.fb.group({
  email: ['', [Validators.required, emailValidator]]
});

HTTP & Observables

Basic HTTP Requests

constructor(private http: HttpClient) {}

// GET
getUsers(): Observable<User[]> {
  return this.http.get<User[]>('/api/users');
}

// POST
createUser(user: User): Observable<User> {
  return this.http.post<User>('/api/users', user);
}

// With headers
const headers = new HttpHeaders().set('Authorization', 'Bearer token');
this.http.get('/api/data', { headers });

Error Handling

getUsers().pipe(
  catchError(error => {
    console.error('Error:', error);
    return throwError(() => error);
  })
).subscribe();

HTTP Interceptor

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const authReq = req.clone({
      setHeaders: { Authorization: `Bearer ${this.auth.getToken()}` }
    });
    return next.handle(authReq);
  }
}

Lifecycle Hooks

Common Lifecycle Hooks

export class MyComponent implements OnInit, OnDestroy {
  ngOnChanges(changes: SimpleChanges) { }      // Input properties change
  ngOnInit() { }                               // Component initialization
  ngDoCheck() { }                              // Change detection runs
  ngAfterContentInit() { }                     // Content projection init
  ngAfterContentChecked() { }                  // After content checked
  ngAfterViewInit() { }                        // View initialized
  ngAfterViewChecked() { }                     // After view checked
  ngOnDestroy() { }                            // Before destruction
}

Best Practices

  • ngOnInit: Initialize data, make HTTP calls
  • ngOnDestroy: Unsubscribe, clear timers
  • ngOnChanges: React to input changes
  • ngAfterViewInit: Access ViewChild elements

Change Detection

Change Detection Strategies

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush
})

Manual Change Detection

constructor(private cd: ChangeDetectorRef) {}

updateData() {
  this.data = newData;
  this.cd.markForCheck();        // OnPush strategy
  // or
  this.cd.detectChanges();        // Force check
}

Pipes

Built-in Pipes

{{ date | date:'short' }}
{{ price | currency:'USD' }}
{{ name | uppercase }}
{{ text | slice:0:10 }}
{{ users | json }}

Custom Pipe

@Pipe({ name: 'exponential' })
export class ExponentialPipe implements PipeTransform {
  transform(value: number, exponent = 1): number {
    return Math.pow(value, exponent);
  }
}
// Usage: {{ 2 | exponential:10 }}

Async Pipe

<div *ngIf="user$ | async as user">
  {{ user.name }}
</div>

RxJS Operators

Common Operators

// map - Transform values
source$.pipe(
  map(x => x * 2)
)

// filter - Filter values
source$.pipe(
  filter(x => x > 10)
)

// tap - Side effects
source$.pipe(
  tap(x => console.log(x))
)

// switchMap - Cancel previous, switch to new
search$.pipe(
  switchMap(term => this.http.get(`/search?q=${term}`))
)

// mergeMap - Handle all
clicks$.pipe(
  mergeMap(() => this.http.get('/api/data'))
)

// debounceTime - Delay emissions
input$.pipe(
  debounceTime(300)
)

// distinctUntilChanged - Emit only unique
input$.pipe(
  distinctUntilChanged()
)

// takeUntil - Unsubscribe pattern
private destroy$ = new Subject<void>();

ngOnInit() {
  this.service.data$.pipe(
    takeUntil(this.destroy$)
  ).subscribe();
}

ngOnDestroy() {
  this.destroy$.next();
  this.destroy$.complete();
}

Performance Optimization

TrackBy Function

trackByFn(index: number, item: any) {
  return item.id;
}
<li *ngFor="let item of items; trackBy: trackByFn">

Lazy Loading Images

<img [src]="imageUrl" loading="lazy">

OnPush Strategy

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush
})

Preloading Strategies

RouterModule.forRoot(routes, {
  preloadingStrategy: PreloadAllModules
})

Bundle Size Optimization

  • Tree shaking
  • Lazy load modules
  • Use production builds
  • Remove unused imports

Testing

Unit Testing (Jasmine/Karma)

describe('Component: Example', () => {
  let component: ExampleComponent;
  let fixture: ComponentFixture<ExampleComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ExampleComponent]
    });
    fixture = TestBed.createComponent(ExampleComponent);
    component = fixture.componentInstance;
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should render title', () => {
    component.title = 'Test';
    fixture.detectChanges();
    const compiled = fixture.nativeElement;
    expect(compiled.querySelector('h1').textContent).toContain('Test');
  });
});

Service Testing

it('should return data', () => {
  const service = TestBed.inject(DataService);
  expect(service.getData()).toEqual(['data1', 'data2']);
});

HTTP Testing

it('should fetch users', () => {
  const httpMock = TestBed.inject(HttpTestingController);
  
  service.getUsers().subscribe(users => {
    expect(users.length).toBe(2);
  });
  
  const req = httpMock.expectOne('/api/users');
  expect(req.request.method).toBe('GET');
  req.flush([{id: 1}, {id: 2}]);
});

Advanced Concepts

Content Projection

<!-- Parent -->
<app-card>
  <h1 card-title>Title</h1>
  <p card-content>Content</p>
</app-card>

<!-- Child template -->
<ng-content select="[card-title]"></ng-content>
<ng-content select="[card-content]"></ng-content>

Dynamic Components

constructor(private viewContainerRef: ViewContainerRef) {}

loadComponent() {
  // Modern approach (Angular 13+)
  const componentRef = this.viewContainerRef.createComponent(DynamicComponent);
  componentRef.instance.data = 'Dynamic Data';
  
  // Legacy approach (pre-Angular 13)
  // const factory = this.componentFactoryResolver.resolveComponentFactory(DynamicComponent);
  // const componentRef = this.viewContainerRef.createComponent(factory);
}

HostBinding & HostListener

@HostBinding('class.active') isActive = false;
@HostListener('click') onClick() {
  this.isActive = !this.isActive;
}

Renderer2 (Safe DOM Manipulation)

constructor(private renderer: Renderer2, private el: ElementRef) {}

changeColor() {
  this.renderer.setStyle(this.el.nativeElement, 'color', 'red');
}

NgZone

constructor(private ngZone: NgZone) {}

runOutsideAngular() {
  this.ngZone.runOutsideAngular(() => {
    // Heavy computation that doesn't need change detection
  });
}

Angular Elements

const element = createCustomElement(ButtonComponent, { injector });
customElements.define('custom-button', element);

Standalone Components (Angular 14+)

@Component({
  selector: 'app-standalone',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: '<p>Standalone component</p>'
})
export class StandaloneComponent { }

// Bootstrap
bootstrapApplication(StandaloneComponent, {
  providers: [
    importProvidersFrom(HttpClientModule)
  ]
});

Signals (Angular 16+)

// Signal creation
name = signal('John');
count = signal(0);

// Computed signals
fullName = computed(() => `${this.firstName()} ${this.lastName()}`);

// Effects
effect(() => {
  console.log(`Count is: ${this.count()}`);
});

// Update signals
updateName() {
  this.name.set('Jane');
  this.count.update(val => val + 1);
}

Key Interview Concepts

Angular Architecture

  • Framework vs Library: Angular is a full framework providing structure, routing, HTTP client, testing utilities
  • MVC Pattern: Model-View-Controller architecture with components as controllers
  • SPA: Single Page Application with client-side routing
  • TypeScript: Strongly typed superset of JavaScript with compile-time error checking

Data Binding Types

  • Interpolation: {{ value }} - One-way, component to template
  • Property Binding: [property]="value" - One-way, component to template
  • Event Binding: (event)="handler()" - One-way, template to component
  • Two-way Binding: [(ngModel)]="value" - Bidirectional data flow

Change Detection Strategy

  • Default: Checks all components on any event
  • OnPush: Only checks when inputs change or events fire
  • Manual: Use ChangeDetectorRef for fine-grained control
  • Immutable Objects: Required for OnPush to work effectively

Dependency Injection Hierarchy

  • Root Level: providedIn: 'root' - Singleton across app
  • Module Level: Providers array in module
  • Component Level: Providers in component metadata
  • Injection Tokens: For non-class dependencies

Observable vs Promise

  • Single vs Multiple: Promise handles one value, Observable handles streams
  • Cancellation: Observables can be unsubscribed, Promises cannot
  • Operators: RxJS operators for transformation, filtering, combination
  • Lazy: Observables are lazy (cold) until subscribed

Security Considerations

  • Sanitization: Angular sanitizes values automatically in templates
  • Trusted Values: Use DomSanitizer for trusted HTML/URLs
  • CSRF Protection: Use HttpClientXsrfModule
  • Content Security Policy: Implement CSP headers

Modern Angular Features

  • Standalone Components: Simplified bootstrapping without NgModules (Angular 14+)
  • Signals: Reactive primitives for fine-grained reactivity (Angular 16+)
  • Control Flow: New @if, @for, @switch syntax (Angular 17+)
  • SSR & Hydration: Improved server-side rendering with client hydration

Development Best Practices

Component Design

  • Single responsibility principle
  • Input/Output for communication
  • Smart (container) vs Dumb (presentational) components
  • Minimal template logic

State Management

  • Services for shared state
  • BehaviorSubject for reactive state
  • NgRx for complex application state
  • Immutable state updates

Performance Optimization

  • OnPush change detection strategy
  • TrackBy functions in ngFor
  • Lazy loading modules and components
  • Tree shaking and dead code elimination
  • Virtual scrolling for large lists
  • Image lazy loading
  • Preloading strategies

Code Organization

  • Feature modules for related functionality
  • Shared modules for common components
  • Core module for singleton services
  • Barrel exports for clean imports

Testing Strategy

  • Unit tests for components and services
  • Integration tests for component interactions
  • E2E tests for critical user flows
  • Test-driven development approach