Angular Quickstart
This tutorial assumes you’re already familiar with HTML, CSS, JS, and TypeScript. It also assumes you know what a JS framework is for.
Online Setup
Section titled “Online Setup”If you prefer to work online, you can open the Angular training ground repo on Stackblitz and start coding right away in your browser. You don’t have to run any of the npm commands if you use Stackblitz. (NOTE: This link works best in Chrome.)
Local Setup
Section titled “Local Setup”If you prefer to work locally, you can follow these steps (you will need Node and npm installed).
First, run this command in your terminal to install the Angular CLI globally.
npm install -g @angular/cli
Then, visit the Angular training ground repo. Press the green Code button and click Download ZIP. Unzip the file and then open it in your editor. Then, run this command in the terminal to install the packages listed in package.json
.
npm install
Then run this command to start the dev server.
npm start
You should see something like this in your terminal.
Watch mode enabled. Watching for file changes...NOTE: Raw file sizes do not reflect development server per-request transformations. ➜ Local: http://localhost:4200/ ➜ press h + enter to show help
Visit localhost:4200 in your browser. The web page should say Hello World.
In your editor, you’ll see several files. However, for now you’ll mainly be working in hello-world.html
.
Hello World
Section titled “Hello World”In our hello-world.html
file, we have an <h1>
element already added.
<h1>Hello World</h1>
Task: To make it more personal, change World
to your name.
<h1>Hello John</h1>
Multiple roots
Section titled “Multiple roots”Unlike some other JS frameworks, you can have more than one root element.
Task: Add a <p>
element after the <h1>
element with some placeholder text.
<h1>Hello John</h1><p>Lorem ipsum dolor...</p>
After saving, you should now see both elements in the browser.
app.ts
Section titled “app.ts”Before we look at our next topic, open up app.ts
so you can see how it’s importing the Hello World component.
import { Component } from '@angular/core';import { RouterOutlet } from '@angular/router';import { HelloWorld } from './hello-world/hello-world';
@Component({ selector: 'app-root', imports: [RouterOutlet, HelloWorld], templateUrl: './app.html', styleUrl: './app.css'})export class App { protected title = 'angular-training-ground';}
You’ll also want to look at app.html
to see how the component is used.
<app-hello-world />
External CSS File
Section titled “External CSS File”You typically add styles using external CSS files. Add the following code to hello-world.css
.
h1 { color: red;}
After saving, you should see the text in the <h1>
element change to red.
class attribute
Section titled “class attribute”You can use a class
attribute just like in normal HTML.
Task: Add a class
attribute with the value of red
. Change the h1
selector to .red
.
<h1 class="red">Hello John</h1><p>Lorem ipsum dolor...</p>
.red { color: red;}
For this section, we’re going to create a new Counter component. Run the following command in your shell.
ng g c counter
This will generate a new folder called counter
inside the app
folder. It will create the following files: counter.css
, counter.html
, counter.spec.ts
, and counter.ts
.
Update app.ts
to import the new component.
import { Component } from '@angular/core';import { RouterOutlet } from '@angular/router';import { HelloWorld } from './hello-world/hello-world';import { Counter } from './counter/counter';
@Component({ selector: 'app-root', imports: [RouterOutlet, HelloWorld, Counter], templateUrl: './app.html', styleUrl: './app.css'})export class App { protected title = 'angular-training-ground';}
Update app.html
to use the new component.
<app-counter />
Make it reactive
Section titled “Make it reactive”Replace the contents of counter.html
with a <button>
that increments a count.
<button (click)="increment()">Count: {{ count() }}</button>
Lastly, you’ll want to add the count
property and the increment()
method to the Counter class.
import { Component, signal, computed } from '@angular/core';
@Component({ selector: 'app-counter', imports: [], templateUrl: './counter.html', styleUrl: './counter.css'})export class Counter { count = signal(0);
increment(): void { this.count.update(current => current + 1); }}
Angular uses signals to make things reactive. The signal()
function makes count
reactive, which means it will automatically update the UI when its value changes.
Conditional
Section titled “Conditional”Let’s add a message that will display when you reach a certain count.
Task: Add an <h1>
element using the @if
template syntax. The <h1>
will show when the count is greater than 10.
@if (count() > 10) { <h1>Good job!</h1>}
<button (click)="increment()">Count: {{ count() }}</button>
Now add an <h1>
element with the @else
template syntax. This <h1>
will show when the count is 10 or less.
@if (count() > 10) { <h1>Good job!</h1> } @else { <h1>Let's get started</h1> }
<button (click)="increment()">Count: {{ count() }}</button>
Now add an <h1>
with a @else if
template syntax in the middle. This message will be displayed when the count is greater than 5 but less than 11.
@if (count() > 10) { <h1>Good job!</h1>} @else if (count() > 5) { <h1>Not bad</h1>} @else { <h1>Let's get started</h1>}
<button (click)="increment()">Count: {{ count() }}</button>
References
Binding
Section titled “Binding”Let’s learn about binding. Binding is the technique that is used to keep the value of an input in sync with a reactive variable.
Let’s reset the Hello World component back to this.
<h1>Hello World</h1>
Then, add the following code.
<input [(ngModel)]="name" /><h1>Hello {{ name() }}</h1>
Also, you’ll need to import the FormsModule
in the HelloWorld component. This is necessary for using ngModel
in Angular. You’ll also need to import the signal
function from @angular/core
and create the name property.
import { Component, signal } from '@angular/core';import { FormsModule } from '@angular/forms';
@Component({ selector: 'app-hello-world', imports: [FormsModule], templateUrl: './hello-world.html', styleUrl: './hello-world.css'})export class HelloWorld { name = signal('');}
The part that says [(ngModel)]="name"
is how you bind the input to the name variable, which is reactive.
Now the heading will update whenever you type in the input.
References
For this section, we’re going to create a new TodoList component. Run the following command in your shell.
ng g c todo-list
This will generate a new folder called todo-list
inside the app
folder. It will create the following files: todo-list.css
, todo-list.html
, todo-list.spec.ts
, and todo-list.ts
.
import { Component } from '@angular/core';import { RouterOutlet } from '@angular/router';import { HelloWorld } from './hello-world/hello-world';import { Counter } from './counter/counter';import { TodoList } from './todo-list/todo-list';
@Component({ selector: 'app-root', imports: [RouterOutlet, HelloWorld, Counter, TodoList], templateUrl: './app.html', styleUrl: './app.css'})export class App { protected title = 'angular-training-ground';}
Then, update app.html
to use the new component.
<app-todo-list />
Create the TypeScript interface for TodoItem
and add the tasks
data.
import { Component, signal } from '@angular/core';
interface TodoItem { text: string; completed: boolean;}
@Component({ selector: 'app-todo-list', imports: [], templateUrl: './todo-list.html',})export class TodoList { todos = signal<TodoItem[]>([ { text: 'Learn Angular basics', completed: false }, { text: 'Build a todo app', completed: false }, { text: 'Practice TypeScript', completed: false } ]);}
Next, replace the contents of todo-list.html
with the following code. Use the @for
template syntax to loop through the list of tasks.
<div><ul> @for (todo of todos(); track $index) { <li> {{ todo.text }} </li> }</ul></div>
References
Add items
Section titled “Add items”To add items to the list, add the <form>
, <input>
, and <button>
elements to todo-list.html
.
<div><form (ngSubmit)="addTodo()" #taskForm="ngForm"> <input [(ngModel)]="newTodo" name="newTodo" > <button type="submit">Add</button></form>
<ul> @for (todo of todos(); track $index) { <li> {{ todo.text }} </li> }</ul></div>
Import the FormsModule
at the top. This is necessary for using ngModel
in Angular. Then create the newTodo
signal and the addTodo()
method in todo-list.ts.
import { Component, signal } from '@angular/core';import { FormsModule } from '@angular/forms';
interface TodoItem { text: string; completed: boolean;}
@Component({ selector: 'app-todo-list', imports: [FormsModule], templateUrl: './todo-list.html',})export class TodoList { todos = signal<TodoItem[]>([ { text: 'Learn Angular basics', completed: false }, { text: 'Build a todo app', completed: false }, { text: 'Practice TypeScript', completed: false } ]);
newTodo = signal('');
addTodo() { this.todos.update(todos => [...todos, { text: this.newTodo(), completed: false }]); this.newTodo.set(''); }}
After saving, you should be able to add items to the list.
Remove items
Section titled “Remove items”To delete an item, first add a <button>
element to each item in the list.
<div><form (ngSubmit)="addTodo()" #taskForm="ngForm"> <input [(ngModel)]="newTodo" name="newTodo" > <button type="submit">Add</button></form>
<ul> @for (todo of todos(); track $index) { <li> {{ todo.text }} <button (click)="deleteTodo($index)">Delete</button> </li> }</ul></div>
Then create the deleteTodo()
method in todo-list.ts
.
import { Component, signal } from '@angular/core';import { FormsModule } from '@angular/forms';
interface TodoItem { text: string; completed: boolean;}
@Component({ selector: 'app-todo-list', imports: [FormsModule], templateUrl: './todo-list.html',})export class TodoList { todos = signal<TodoItem[]>([ { text: 'Learn Angular basics', completed: false }, { text: 'Build a todo app', completed: false }, { text: 'Practice TypeScript', completed: false } ]);
newTodo = signal('');
addTodo() { this.todos.update(todos => [...todos, { text: this.newTodo(), completed: false }]); this.newTodo.set(''); }
deleteTodo(index: number) { const todoToDelete = this.todos()[index]; this.todos.update(todos => todos.filter(todo => todo !== todoToDelete)); }}
After saving, you should be able to delete items from the list.
Filter items
Section titled “Filter items”To filter the items, first add a checkbox <input>
element at the top that can hide completed items. Then add a checkbox <input>
to each task that can toggle its completion status.
<div><form (ngSubmit)="addTodo()" #taskForm="ngForm"> <input [(ngModel)]="newTodo" name="newTodo" > <button type="submit">Add Task</button></form>
<div> <label> <input type="checkbox" [(ngModel)]="hideCompleted" name="hideCompleted" > Hide completed items </label></div>
<ul> @for (todo of visibleTodos(); track $index) { <li> <input type="checkbox" [checked]="todo.completed" (change)="toggleTodo($index)" > {{ todo.text }} <button (click)="deleteTodo($index)">Delete</button> </li> }</ul></div>
Add the hideCompleted
signal and the visibleTodos
computed property in todo-list.ts
. Then add the toggleTodo
method to handle toggling the completion status of a task.
import { Component, signal, computed } from '@angular/core';import { FormsModule } from '@angular/forms';
interface TodoItem { text: string; completed: boolean;}
@Component({ selector: 'app-todo-list', imports: [FormsModule], templateUrl: './todo-list.html',})export class TodoList { todos = signal<TodoItem[]>([ { text: 'Learn Angular basics', completed: false }, { text: 'Build a todo app', completed: false }, { text: 'Practice TypeScript', completed: false } ]);
newTodo = signal(''); hideCompleted = signal(false);
visibleTodos = computed(() => { const allTodos = this.todos(); return this.hideCompleted() ? allTodos.filter(todo => !todo.completed) : allTodos; });
addTodo() { const todoText = this.newTodo().trim(); if (todoText) { this.todos.update(todos => [...todos, { text: todoText, completed: false }]); this.newTodo.set(''); } }
deleteTodo(index: number) { const todoToDelete = this.visibleTodos()[index]; this.todos.update(todos => todos.filter(todo => todo !== taskToDelete)); }
toggleTodo(index: number) { const todoToToggle = this.visibleTodos()[index]; this.todos.update(todos => todos.map(todo => todo === todoToToggle ? { ...todo, completed: !todo.completed } : todo ) ); }}
After saving, you should be able to hide completed items.
References
Input properties
Section titled “Input properties”To make the HelloWorld component more flexible, let’s add an input property for the name. This way, you can pass a name to the component from the parent.
First, replace the name you added before with the following code.
<h1>Hello {{ name() }}</h1>
Then add the name
input property to the Hello World component. This will allow you to pass a name to the component from the parent.
<app-hello-world name="John" />
Finally, import the input
function from @angular/core
and use it to create the name
input property in the HelloWorld class.
import { Component, input } from "@angular/core";
@Component({ selector: "app-hello-world", imports: [], templateUrl: "./hello-world.html", styleUrl: "./hello-world.css",})export class HelloWorld { name = input<string>();}
AI & Prompting
Section titled “AI & Prompting”The Angular docs have a guide on prompting you can follow.
As an example, we created a custom Angular GPT that you can try. It already has the instructions from Angular’s prompting guide loaded into it.