Angular Basics Cheat Sheet: Essential Concepts for Beginners

Introduction

Angular is a popular TypeScript-based web application framework developed and maintained by Google. It provides a comprehensive solution for building single-page applications with a component-based architecture. This cheat sheet covers the essential concepts and techniques you need to know when starting with Angular.

Setting Up Angular

Installing Angular CLI

The Angular CLI (Command Line Interface) is the official tool for initializing, developing, and maintaining Angular applications.

# Install Angular CLI globally
npm install -g @angular/cli

# Check installation and version
ng version

Creating a New Project

# Create a new Angular project
ng new my-project-name

# Create with specific options
ng new my-project-name --routing --style=scss

Running Your Application

# Navigate to your project directory
cd my-project-name

# Start the development server
ng serve

# Start and automatically open in browser
ng serve --open
# or
ng serve -o

Angular Project Structure

my-project-name/
├── node_modules/         # Third-party libraries
├── src/                  # Source files
│   ├── app/              # Application code
│   │   ├── app.component.ts       # Root component
│   │   ├── app.component.html     # Root component template
│   │   ├── app.component.css      # Root component styles
│   │   ├── app.component.spec.ts  # Root component tests
│   │   ├── app.module.ts          # Root module
│   ├── assets/           # Static assets (images, etc.)
│   ├── environments/     # Environment configuration
│   ├── index.html        # Main HTML file
│   ├── main.ts           # Entry point
│   ├── styles.css        # Global styles
├── angular.json          # Angular workspace configuration
├── package.json          # NPM dependencies and scripts
├── tsconfig.json         # TypeScript configuration

Angular Building Blocks

Components

Components are the main building blocks of Angular applications. They control a section of the UI.

Creating a Component

# Generate a new component using Angular CLI
ng generate component component-name
# or shorter syntax
ng g c component-name

Component Structure

// my-component.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponent {
  // Properties
  title = 'My Component';
  items = ['Item 1', 'Item 2', 'Item 3'];
  
  // Methods
  handleClick() {
    alert('Button clicked!');
  }
}
<!-- my-component.component.html -->
<h1>{{ title }}</h1>
<ul>
  <li *ngFor="let item of items">{{ item }}</li>
</ul>
<button (click)="handleClick()">Click me</button>

Modules

Modules in Angular help organize the application into cohesive blocks of functionality.

Basic Module Structure

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { MyComponent } from './my-component/my-component.component';

@NgModule({
  declarations: [
    AppComponent,
    MyComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Services

Services provide functionality that’s not directly related to views. They are used for data fetching, logging, and other operations.

Creating a Service

# Generate a new service using Angular CLI
ng generate service service-name
# or shorter syntax
ng g s service-name

Basic Service Structure

// data.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'  // This makes the service a singleton
})
export class DataService {
  private data = ['Item 1', 'Item 2', 'Item 3'];
  
  constructor() { }
  
  getData() {
    return this.data;
  }
  
  addData(item: string) {
    this.data.push(item);
  }
}

Using a Service in a Component

// my-component.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from '../data.service';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html'
})
export class MyComponent implements OnInit {
  items: string[] = [];
  
  // Inject the service in the constructor
  constructor(private dataService: DataService) { }
  
  ngOnInit(): void {
    // Get data from service when component initializes
    this.items = this.dataService.getData();
  }
  
  addItem(newItem: string) {
    this.dataService.addData(newItem);
    this.items = this.dataService.getData();
  }
}

Data Binding

Angular offers several ways to connect your component’s data to its template:

Interpolation (One-way: Component to View)

<h1>{{ title }}</h1>
<p>Welcome, {{ user.name }}</p>
<p>{{ 'Hello, ' + user.name }}</p>
<p>{{ 2 + 2 }}</p> <!-- Displays: 4 -->

Property Binding (One-way: Component to View)

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

Event Binding (One-way: View to Component)

<button (click)="handleClick()">Click me</button>
<input (keyup)="handleKeyUp($event)">

Two-way Binding (using ngModel)

First, import FormsModule in your module:

// app.module.ts
import { FormsModule } from '@angular/forms';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule  // Add this
  ],
  // ...
})

Then use it in your template:

<input [(ngModel)]="name">
<p>Hello, {{ name }}</p>

Directives

Directives are classes that add behavior to elements in your Angular applications.

Built-in Structural Directives

These change the DOM layout by adding and removing elements.

ngIf (Conditional Rendering)

<!-- Simple condition -->
<div *ngIf="isVisible">This element will show if isVisible is true</div>

<!-- With else block -->
<div *ngIf="isVisible; else notVisible">Content when visible</div>
<ng-template #notVisible>Content when not visible</ng-template>

<!-- With then and else -->
<div *ngIf="isVisible; then visibleBlock else notVisibleBlock"></div>
<ng-template #visibleBlock>Content when visible</ng-template>
<ng-template #notVisibleBlock>Content when not visible</ng-template>

ngFor (List Rendering)

<!-- Basic usage -->
<ul>
  <li *ngFor="let item of items">{{ item }}</li>
</ul>

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

<!-- With additional variables -->
<ul>
  <li *ngFor="let item of items; let i = index; let isFirst = first; let isLast = last">
    {{ i }}: {{ item }} 
    <span *ngIf="isFirst">(First item)</span>
    <span *ngIf="isLast">(Last item)</span>
  </li>
</ul>

ngSwitch

<div [ngSwitch]="color">
  <p *ngSwitchCase="'red'">The color is red</p>
  <p *ngSwitchCase="'blue'">The color is blue</p>
  <p *ngSwitchCase="'green'">The color is green</p>
  <p *ngSwitchDefault>The color is something else</p>
</div>

Attribute Directives

These change the appearance or behavior of an existing element.

ngClass

<!-- Single class with condition -->
<div [ngClass]="{'active': isActive}">This div is active</div>

<!-- Multiple classes -->
<div [ngClass]="{'active': isActive, 'disabled': isDisabled, 'special': isSpecial}">
  This div has multiple conditional classes
</div>

<!-- Using component method -->
<div [ngClass]="getClasses()">Classes from method</div>
getClasses() {
  return {
    'active': this.isActive,
    'disabled': this.isDisabled,
    'special': this.isSpecial
  };
}

ngStyle

<!-- Single style with condition -->
<div [ngStyle]="{'color': textColor}">This text has dynamic color</div>

<!-- Multiple styles -->
<div [ngStyle]="{'color': textColor, 'font-size': fontSize + 'px', 'font-weight': isBold ? 'bold' : 'normal'}">
  This div has multiple dynamic styles
</div>

<!-- Using component method -->
<div [ngStyle]="getStyles()">Styles from method</div>
getStyles() {
  return {
    'color': this.textColor,
    'font-size': this.fontSize + 'px',
    'font-weight': this.isBold ? 'bold' : 'normal'
  };
}

Pipes

Pipes transform values for display in your templates.

Built-in Pipes

<!-- Date pipe -->
<p>{{ today | date }}</p>                   <!-- Apr 15, 2023 -->
<p>{{ today | date:'shortDate' }}</p>       <!-- 4/15/23 -->
<p>{{ today | date:'fullDate' }}</p>        <!-- Saturday, April 15, 2023 -->
<p>{{ today | date:'MM/dd/yyyy' }}</p>      <!-- 04/15/2023 -->

<!-- Uppercase/Lowercase -->
<p>{{ name | uppercase }}</p>               <!-- JOHN DOE -->
<p>{{ name | lowercase }}</p>               <!-- john doe -->

<!-- Number formatting -->
<p>{{ price | number }}</p>                 <!-- 1,234.56 -->
<p>{{ price | number:'1.2-2' }}</p>         <!-- 1,234.56 -->

<!-- Currency -->
<p>{{ price | currency }}</p>               <!-- $1,234.56 -->
<p>{{ price | currency:'EUR' }}</p>         <!-- €1,234.56 -->

<!-- Percent -->
<p>{{ 0.75 | percent }}</p>                 <!-- 75% -->

<!-- Slice -->
<p>{{ [1,2,3,4,5] | slice:1:3 }}</p>        <!-- [2,3] -->

<!-- JSON -->
<pre>{{ user | json }}</pre>                <!-- Pretty-printed JSON -->

<!-- Chaining pipes -->
<p>{{ today | date:'fullDate' | uppercase }}</p>

Creating a Custom Pipe

# Generate a new pipe using Angular CLI
ng generate pipe pipe-name
# or shorter syntax
ng g p pipe-name
// truncate.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'truncate'
})
export class TruncatePipe implements PipeTransform {
  transform(value: string, limit: number = 25, trail: string = '...'): string {
    if (!value) {
      return '';
    }
    
    if (value.length <= limit) {
      return value;
    }
    
    return value.substring(0, limit) + trail;
  }
}

In your template:

<p>{{ longText | truncate:10:'...' }}</p>

Component Communication

Parent to Child: @Input

Parent component:

// parent.component.ts
@Component({
  selector: 'app-parent',
  template: `
    <h1>Parent Component</h1>
    <app-child [data]="parentData" [message]="'Hello from parent'"></app-child>
  `
})
export class ParentComponent {
  parentData = [1, 2, 3, 4, 5];
}

Child component:

// child.component.ts
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `
    <h2>Child Component</h2>
    <p>Message: {{ message }}</p>
    <ul>
      <li *ngFor="let item of data">{{ item }}</li>
    </ul>
  `
})
export class ChildComponent {
  @Input() data: number[] = [];
  @Input() message: string = '';
}

Child to Parent: @Output

Child component:

// child.component.ts
import { Component, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `
    <h2>Child Component</h2>
    <button (click)="sendMessage()">Send Message to Parent</button>
    <button (click)="sendData()">Send Data to Parent</button>
  `
})
export class ChildComponent {
  @Output() messageEvent = new EventEmitter<string>();
  @Output() dataEvent = new EventEmitter<number>();
  
  sendMessage() {
    this.messageEvent.emit('Hello from child component');
  }
  
  sendData() {
    this.dataEvent.emit(42);
  }
}

Parent component:

// parent.component.ts
@Component({
  selector: 'app-parent',
  template: `
    <h1>Parent Component</h1>
    <p>Message from child: {{ childMessage }}</p>
    <p>Data from child: {{ childData }}</p>
    <app-child 
      (messageEvent)="receiveMessage($event)"
      (dataEvent)="receiveData($event)">
    </app-child>
  `
})
export class ParentComponent {
  childMessage = '';
  childData = 0;
  
  receiveMessage(message: string) {
    this.childMessage = message;
  }
  
  receiveData(data: number) {
    this.childData = data;
  }
}

Forms

Angular provides two approaches to forms: Template-driven forms (simpler) and Reactive forms (more robust).

Template-driven Forms

First, import FormsModule:

// app.module.ts
import { FormsModule } from '@angular/forms';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule
  ],
  // ...
})
export class AppModule { }

Then create your form:

<!-- template-form.component.html -->
<form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">
  <div>
    <label for="name">Name</label>
    <input 
      type="text" 
      id="name" 
      name="name" 
      [(ngModel)]="model.name" 
      required 
      #name="ngModel">
    <div *ngIf="name.invalid && (name.dirty || name.touched)">
      Name is required.
    </div>
  </div>
  
  <div>
    <label for="email">Email</label>
    <input 
      type="email" 
      id="email" 
      name="email" 
      [(ngModel)]="model.email" 
      required 
      email 
      #email="ngModel">
    <div *ngIf="email.invalid && (email.dirty || email.touched)">
      <div *ngIf="email.errors?.['required']">Email is required.</div>
      <div *ngIf="email.errors?.['email']">Email is invalid.</div>
    </div>
  </div>
  
  <button type="submit" [disabled]="myForm.invalid">Submit</button>
</form>

<div *ngIf="submitted">
  <h2>Submitted Values</h2>
  <p>Name: {{ model.name }}</p>
  <p>Email: {{ model.email }}</p>
</div>
// template-form.component.ts
import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({
  selector: 'app-template-form',
  templateUrl: './template-form.component.html'
})
export class TemplateFormComponent {
  model = {
    name: '',
    email: ''
  };
  
  submitted = false;
  
  onSubmit(form: NgForm) {
    if (form.valid) {
      console.log('Form submitted', this.model);
      this.submitted = true;
    }
  }
}

Reactive Forms

First, import ReactiveFormsModule:

// app.module.ts
import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
  imports: [
    BrowserModule,
    ReactiveFormsModule
  ],
  // ...
})
export class AppModule { }

Then create your form:

// reactive-form.component.ts
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';

@Component({
  selector: 'app-reactive-form',
  templateUrl: './reactive-form.component.html'
})
export class ReactiveFormComponent implements OnInit {
  userForm: FormGroup;
  submitted = false;
  
  constructor(private fb: FormBuilder) { }
  
  ngOnInit() {
    this.userForm = this.fb.group({
      name: ['', [Validators.required, Validators.minLength(3)]],
      email: ['', [Validators.required, Validators.email]],
      address: this.fb.group({
        street: [''],
        city: ['', Validators.required],
        zip: ['', [Validators.required, Validators.pattern(/^\d{5}$/)]]
      })
    });
  }
  
  onSubmit() {
    if (this.userForm.valid) {
      console.log('Form submitted', this.userForm.value);
      this.submitted = true;
    } else {
      // Mark all fields as touched to trigger validation
      this.markFormGroupTouched(this.userForm);
    }
  }
  
  // Helper method to mark all controls as touched
  markFormGroupTouched(formGroup: FormGroup) {
    Object.values(formGroup.controls).forEach(control => {
      control.markAsTouched();
      
      if (control instanceof FormGroup) {
        this.markFormGroupTouched(control);
      }
    });
  }
}
<!-- reactive-form.component.html -->
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
  <div>
    <label for="name">Name</label>
    <input type="text" id="name" formControlName="name">
    <div *ngIf="userForm.get('name')?.invalid && userForm.get('name')?.touched">
      <div *ngIf="userForm.get('name')?.errors?.['required']">Name is required.</div>
      <div *ngIf="userForm.get('name')?.errors?.['minlength']">
        Name must be at least 3 characters.
      </div>
    </div>
  </div>
  
  <div>
    <label for="email">Email</label>
    <input type="email" id="email" formControlName="email">
    <div *ngIf="userForm.get('email')?.invalid && userForm.get('email')?.touched">
      <div *ngIf="userForm.get('email')?.errors?.['required']">Email is required.</div>
      <div *ngIf="userForm.get('email')?.errors?.['email']">Email is invalid.</div>
    </div>
  </div>
  
  <div formGroupName="address">
    <h3>Address</h3>
    
    <div>
      <label for="street">Street</label>
      <input type="text" id="street" formControlName="street">
    </div>
    
    <div>
      <label for="city">City</label>
      <input type="text" id="city" formControlName="city">
      <div *ngIf="userForm.get('address.city')?.invalid && userForm.get('address.city')?.touched">
        City is required.
      </div>
    </div>
    
    <div>
      <label for="zip">ZIP Code</label>
      <input type="text" id="zip" formControlName="zip">
      <div *ngIf="userForm.get('address.zip')?.invalid && userForm.get('address.zip')?.touched">
        <div *ngIf="userForm.get('address.zip')?.errors?.['required']">ZIP code is required.</div>
        <div *ngIf="userForm.get('address.zip')?.errors?.['pattern']">
          ZIP code must be 5 digits.
        </div>
      </div>
    </div>
  </div>
  
  <button type="submit">Submit</button>
</form>

<div *ngIf="submitted">
  <h2>Submitted Values</h2>
  <pre>{{ userForm.value | json }}</pre>
</div>

Basic Routing

First, create a routing module (usually done when creating a project with --routing flag):

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { ContactComponent } from './contact/contact.component';
import { NotFoundComponent } from './not-found/not-found.component';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'about', component: AboutComponent },
  { path: 'contact', component: ContactComponent },
  { path: '**', component: NotFoundComponent }  // Wildcard route for 404
];

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

Import the routing module:

// app.module.ts
import { AppRoutingModule } from './app-routing.module';

@NgModule({
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  // ...
})
export class AppModule { }

Add the router outlet in your app component:

<!-- app.component.html -->
<header>
  <nav>
    <ul>
      <li><a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">Home</a></li>
      <li><a routerLink="/about" routerLinkActive="active">About</a></li>
      <li><a routerLink="/contact" routerLinkActive="active">Contact</a></li>
    </ul>
  </nav>
</header>

<main>
  <!-- This is where route components will be displayed -->
  <router-outlet></router-outlet>
</main>

<footer>
  <p>&copy; 2023 My Angular App</p>
</footer>

HTTP Client

First, import the HttpClientModule:

// app.module.ts
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  // ...
})
export class AppModule { }

Create a service to make HTTP requests:

// data.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

interface Post {
  id: number;
  title: string;
  body: string;
  userId: number;
}

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private apiUrl = 'https://jsonplaceholder.typicode.com';
  
  constructor(private http: HttpClient) { }
  
  getPosts(): Observable<Post[]> {
    return this.http.get<Post[]>(`${this.apiUrl}/posts`);
  }
  
  getPost(id: number): Observable<Post> {
    return this.http.get<Post>(`${this.apiUrl}/posts/${id}`);
  }
  
  createPost(post: Omit<Post, 'id'>): Observable<Post> {
    return this.http.post<Post>(`${this.apiUrl}/posts`, post);
  }
  
  updatePost(id: number, post: Partial<Post>): Observable<Post> {
    return this.http.put<Post>(`${this.apiUrl}/posts/${id}`, post);
  }
  
  deletePost(id: number): Observable<unknown> {
    return this.http.delete(`${this.apiUrl}/posts/${id}`);
  }
}

Use the service in a component:

// posts.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from '../data.service';

interface Post {
  id: number;
  title: string;
  body: string;
  userId: number;
}

@Component({
  selector: 'app-posts',
  templateUrl: './posts.component.html'
})
export class PostsComponent implements OnInit {
  posts: Post[] = [];
  loading = false;
  error = '';
  
  constructor(private dataService: DataService) { }
  
  ngOnInit(): void {
    this.loading = true;
    this.dataService.getPosts().subscribe({
      next: (data) => {
        this.posts = data;
        this.loading = false;
      },
      error: (err) => {
        this.error = 'Failed to load posts';
        this.loading = false;
        console.error(err);
      }
    });
  }
  
  createPost() {
    const newPost = {
      title: 'New Post',
      body: 'This is a new post created via HTTP',
      userId: 1
    };
    
    this.dataService.createPost(newPost).subscribe({
      next: (post) => {
        this.posts.unshift(post);
      },
      error: (err) => {
        console.error('Error creating post', err);
      }
    });
  }
  
  deletePost(id: number) {
    this.dataService.deletePost(id).subscribe({
      next: () => {
        this.posts = this.posts.filter(post => post.id !== id);
      },
      error: (err) => {
        console.error('Error deleting post', err);
      }
    });
  }
}
<!-- posts.component.html -->
<h1>Posts</h1>

<div *ngIf="loading">Loading posts...</div>
<div *ngIf="error">{{ error }}</div>

<button (click)="createPost()">Create New Post</button>

<div *ngIf="!loading && !error">
  <div *ngFor="let post of posts" class="post">
    <h2>{{ post.title }}</h2>
    <p>{{ post.body }}</p>
    <button (click)="deletePost(post.id)">Delete</button>
  </div>
</div>

Common Angular CLI Commands

# Generate components, services, etc.
ng generate component my-component
ng generate service my-service
ng generate pipe my-pipe
ng generate directive my-directive
ng generate module my-module
ng generate interface my-interface
ng generate enum my-enum
ng generate class my-class
ng generate guard my-guard

# Build the application
ng build                  # Development build
ng build --configuration production  # Production build

# Run unit tests
ng test

# Run end-to-end tests
ng e2e

# Lint your code
ng lint

# Update Angular and dependencies
ng update

# Add a dependency
ng add @angular/material

Best Practices for Beginners

  1. Organize by Feature: Group related components, services, and other files into feature modules
  2. Keep Components Small: Each component should have a single responsibility
  3. Use OnPush Change Detection: For better performance, especially in larger applications
  4. Unsubscribe from Observables: Prevent memory leaks by unsubscribing from observables in ngOnDestroy
  5. Use Async Pipe: Let Angular handle subscribing and unsubscribing to observables in templates
  6. Lazy Load Modules: For larger applications, improve initial load time
  7. Proper Error Handling: Always handle errors in HTTP requests and other asynchronous operations
  8. Follow Angular Style Guide: For consistent code structure and naming conventions

Resources for Further Learning

Official Documentation

Online Courses and Tutorials

  • Angular University (angular-university.io)
  • Pluralsight Angular courses
  • Udemy – Angular: The Complete Guide

Books

  • “Angular: Up and Running” by Shyam Seshadri
  • “Angular in Action” by Jeremy Wilken
  • “ng-book: The Complete Book on Angular” by Nathan Murray et al.

Tools

  • Angular DevTools (Chrome Extension)
  • Augury (Chrome Extension for debugging)
  • VS Code with Angular Language Service extension
Scroll to Top