Angular Cheat Sheet
Quick reference for Angular 17+ — standalone components, signals, templates, directives, routing, forms, pipes, and lifecycle hooks. All essential patterns in one page.
Components
Templates & Directives
Services & Dependency Injection
Routing
Forms
Pipes
Lifecycle Hooks
Components
| @Component({ standalone: true, template: `...` }) | Standalone component (Angular 17+) |
| @Component({ selector: "app-hero", templateUrl: "./hero.html" }) | Component with external template |
| @Component({ imports: [CommonModule, RouterModule] }) | Import dependencies in standalone component |
| @Input() title: string = "" | Input property binding |
| @Input({ required: true }) id!: number | Required input (Angular 16+) |
| @Output() clicked = new EventEmitter<string>() | Output event emitter |
| input.required<string>() | Signal-based required input (Angular 17+) |
| output<string>() | Signal-based output (Angular 17+) |
| viewChild.required<ElementRef>("myEl") | Signal-based view child query |
| changeDetection: ChangeDetectionStrategy.OnPush | OnPush change detection for performance |
Templates & Directives
| {{ expression }} | Interpolation binding |
| [property]="value" | Property binding |
| (event)="handler($event)" | Event binding |
| [(ngModel)]="name" | Two-way binding |
| @if (condition) { ... } @else { ... } | Built-in control flow (Angular 17+) |
| @for (item of items; track item.id) { ... } | Built-in for loop with track (Angular 17+) |
| @switch (value) { @case (1) { ... } } | Built-in switch (Angular 17+) |
| @defer { ... } @loading { ... } | Deferrable views — lazy load blocks (Angular 17+) |
| <ng-content select="[header]" /> | Content projection with selector |
| <ng-template #tpl let-item>...</ng-template> | Reusable template reference |
Services & Dependency Injection
| @Injectable({ providedIn: "root" }) | Tree-shakable singleton service |
| constructor(private http: HttpClient) {} | Constructor injection |
| inject(HttpClient) | Functional inject() — no constructor needed |
| { provide: API_URL, useValue: "/api" } | Value provider with InjectionToken |
| { provide: Logger, useClass: FileLogger } | Class provider — swap implementation |
| { provide: DataService, useFactory: () => new DataService(inject(HttpClient)) } | Factory provider |
| { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } | Multi provider for interceptors |
| provideHttpClient(withInterceptors([authInterceptor])) | Functional HTTP interceptors (Angular 17+) |
Routing
| provideRouter(routes) | Provide routes in app config (standalone) |
| { path: "users/:id", component: UserComponent } | Route with parameter |
| { path: "admin", loadComponent: () => import("./admin") } | Lazy-load standalone component |
| { path: "shop", loadChildren: () => import("./shop/routes") } | Lazy-load child routes |
| { path: "**", component: NotFoundComponent } | Wildcard / 404 route |
| { path: "old", redirectTo: "new", pathMatch: "full" } | Redirect route |
| const id = this.route.snapshot.paramMap.get("id") | Read route param (snapshot) |
| this.route.paramMap.subscribe(p => p.get("id")) | Observe route param changes |
| this.router.navigate(["/users", id]) | Programmatic navigation |
| canActivate: [AuthGuard] | Route guard for auth protection |
Forms
| FormsModule + [(ngModel)]="name" | Template-driven form binding |
| fb.group({ name: ["", Validators.required] }) | Reactive form group with FormBuilder |
| fb.nonNullable.group({ email: "" }) | Non-nullable form builder (Angular 14+) |
| new FormControl("", { validators: Validators.email }) | Form control with validator |
| form.get("name")?.value | Read form control value |
| form.patchValue({ name: "Alice" }) | Partial update of form values |
| form.valid / form.invalid / form.dirty | Form state checks |
| Validators.compose([Validators.required, Validators.minLength(3)]) | Compose multiple validators |
| <div *ngIf="name.errors?.required">Required</div> | Show validation error message |
Pipes
| {{ price | currency:"USD" }} | Currency formatting |
| {{ today | date:"yyyy-MM-dd" }} | Date formatting |
| {{ name | uppercase }} | Transform to uppercase |
| {{ name | lowercase }} | Transform to lowercase |
| {{ text | slice:0:100 }} | Slice string or array |
| {{ value | json }} | JSON stringify for debugging |
| {{ data$ | async }} | Subscribe to Observable in template |
| {{ items | keyvalue }} | Iterate object as key-value pairs |
| @Pipe({ name: "truncate", standalone: true }) | Custom standalone pipe |
Lifecycle Hooks
| ngOnInit() | Component initialized — fetch data here |
| ngOnChanges(changes: SimpleChanges) | Input property changed |
| ngOnDestroy() | Cleanup subscriptions and listeners |
| ngAfterViewInit() | View (and children) fully rendered |
| ngAfterContentInit() | Projected content initialized |
| ngDoCheck() | Custom change detection logic |
| effect(() => { console.log(this.count()) }) | Signal effect — runs on signal change (Angular 17+) |
| computed(() => this.price() * this.qty()) | Computed signal — derived reactive value (Angular 17+) |
Step-by-Step Guide
Read Guide →