Skip to content

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.

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.)

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.

Terminal window
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.

Terminal window
npm install

Then run this command to start the dev server.

Terminal window
npm start

You should see something like this in your terminal.

Terminal window
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.

In our hello-world.html file, we have an <h1> element already added.

src/app/hello-world/hello-world.html
<h1>Hello World</h1>

Task: To make it more personal, change World to your name.

src/app/hello-world/hello-world.html
<h1>Hello John</h1>

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.

src/app/hello-world/hello-world.html
<h1>Hello John</h1>
<p>Lorem ipsum dolor...</p>

After saving, you should now see both elements in the browser.

Before we look at our next topic, open up app.ts so you can see how it’s importing the Hello World component.

src/app/app.ts
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.

src/app/app.html
<app-hello-world />

You typically add styles using external CSS files. Add the following code to hello-world.css.

src/app/hello-world/hello-world.css
h1 {
color: red;
}

After saving, you should see the text in the <h1> element change to red.

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.

src/app/hello-world/hello-world.html
<h1 class="red">Hello John</h1>
<p>Lorem ipsum dolor...</p>
src/app/hello-world/hello-world.css
.red {
color: red;
}

For this section, we’re going to create a new Counter component. Run the following command in your shell.

Terminal window
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.

src/app/app.ts
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.

src/app/app.html
<app-counter />

Replace the contents of counter.html with a <button> that increments a count.

src/app/counter/counter.html
<button (click)="increment()">Count: {{ count() }}</button>

Lastly, you’ll want to add the count property and the increment() method to the Counter class.

src/app/counter/counter.ts
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.

References

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.

src/app/counter/counter.html
@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.

src/app/counter/counter.html
@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.

src/app/counter/counter.html
@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

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.

src/app/hello-world/hello-world.html
<h1>Hello World</h1>

Then, add the following code.

src/app/hello-world/hello-world.html
<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.

src/app/hello-world/hello-world.ts
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.

Terminal window
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.

src/app/app.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.

src/app/app.html
<app-todo-list />

Create the TypeScript interface for TodoItem and add the tasks data.

src/app/todo-list/todo-list.ts
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.

src/app/todo-list/todo-list.html
<div>
<ul>
@for (todo of todos(); track $index) {
<li>
{{ todo.text }}
</li>
}
</ul>
</div>
References

To add items to the list, add the <form>, <input>, and <button> elements to todo-list.html.

src/app/todo-list/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.

src/app/todo-list/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.

To delete an item, first add a <button> element to each item in the list.

src/app/todo-list/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 }}
<button (click)="deleteTodo($index)">Delete</button>
</li>
}
</ul>
</div>

Then create the deleteTodo() method in todo-list.ts.

src/app/todo-list/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.

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.

src/app/todo-list/todo-list.html
<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.

src/app/todo-list/todo-list.ts
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

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.

src/app/hello-world/hello-world.html
<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.

src/app/app.html
<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.

src/app/hello-world/hello-world.ts
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>();
}
References

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.