1. Create Project
New Project with CSS, and disabled SSR
ng new vehicle-rental
2. Add a Component Manually
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>VehicleRental</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-header></app-header>
<app-root></app-root>
</body>
</html>
header.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-header',
standalone: true,
templateUrl: './header.component.html'
})
export class HeaderComponent {
}
header.component.html
<header>
<h1>EasyRent</h1>
</header>
main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
import { HeaderComponent } from './app/header.component';
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
bootstrapApplication(HeaderComponent);
-
JETZT SIEHT MAN DEN HEADER
Wir wollen die Komponenten hierarchisch aufbauen, daher soll der header in der app-component sein. |
main.ts - löschen der header component und ebenso in index.html löschen des header tags
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
app.component.html
<app-header></app-header>
app.component.ts
import { Component } from '@angular/core';
import {HeaderComponent} from './header.component';
@Component({
selector: 'app-root',
// standalone: true, // ab Angular 19 default auf true
imports: [HeaderComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
title = 'vehicle-rental';
}
header.component.css
header {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
width: 90%;
max-width: 50rem;
margin: 0 auto 2rem auto;
text-align: center;
background: linear-gradient(
to bottom,
#2c0a4c,
#450d80
);
padding: 1rem;
border-bottom-right-radius: 12px;
border-bottom-left-radius: 12px;
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.6);
}
img {
width: 3.5rem;
object-fit: contain;
}
h1 {
font-size: 1.25rem;
margin: 0;
padding: 0;
}
p {
margin: 0;
font-size: 0.8rem;
text-wrap: balance;
}
@media (min-width: 768px) {
header {
padding: 2rem;
}
img {
width: 4.5rem;
}
h1 {
font-size: 1.5rem;
margin: 0;
padding: 0;
}
}
-
copy the file
rent-a-car-logo-2.png
into the public-folder
header.component.html
<header>
<img src="rent-a-car-logo-3.png" alt="A vehicle rent list"/>
<h1>EasyRent</h1>
<p>Enterprise-level car rent management without friction</p>
</header>
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>VehicleRental</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap"
rel="stylesheet"
/>
</head>
<body>
<app-root></app-root>
</body>
</html>

Figure 1. add image to public folder

-
Now create a
header
-Folder a refactor to move the header files into it.
3. Generate a component
ng g c vehicle

vehicle.component.css
div {
border-radius: 6px;
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.35rem 0.5rem;
background-color: #433352;
color: #c3b3d1;
border: none;
font: inherit;
cursor: pointer;
width: 100%;
min-width: 10rem;
text-align: left;
}
button:hover,
button:active,
.active {
background-color: #9965dd;
color: #150722;
}
img {
width: 2rem;
object-fit: contain;
border-radius: 50%;
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.3);
}
span {
margin: 0;
padding: 0;
font-size: 0.8rem;
font-weight: normal;
}
app.component.css
main {
width: 90%;
max-width: 50rem;
margin: 2.5rem auto;
display: grid;
grid-auto-flow: row;
gap: 2rem;
}
#vehicles {
list-style: none;
margin: 0;
padding: 0;
display: flex;
gap: 0.5rem;
overflow: auto;
}
#fallback {
font-weight: bold;
font-size: 1.15rem;
margin: 0;
text-align: center;
}
@media (min-width: 768px) {
main {
margin: 4rem auto;
grid-template-columns: 1fr 3fr;
}
#vehicles {
flex-direction: column;
}
#fallback {
font-size: 1.5rem;
text-align: left;
}
}
-
app.component.html
-
Strg+Space, Return
-
Return
<app-header />
<app-vehicle />

4. Output Dynamic Content
dummy-vehicles.ts
export const DUMMY_VEHICLES = [
{
id: 'v1',
brand: 'VW Golf',
avatar: 'compact.png'
},
{
id: 'v2',
brand: 'Honda Civic',
avatar: 'coupe.png'
},
{
id: 'v3',
brand: 'Renault Espace',
avatar: 'family.png'
},
{
id: 'v4',
brand: 'Opel Kapitän',
avatar: 'limousine.png'
},
{
id: 'v5',
brand: 'Remault Clio',
avatar: 'smallcar.png'
},
{
id: 'v6',
brand: 'Ford Mustang',
avatar: 'sportscar.png'
},
{
id: 'v7',
brand: 'Dodge Warlock',
avatar: 'truck.png'
},
]
vehicle.component.ts
import { Component } from '@angular/core';
import { DUMMY_VEHICLES } from '../dummy-vehicles';
const randomIndex = Math.floor(Math.random() * DUMMY_VEHICLES.length);
@Component({
selector: 'app-vehicle',
imports: [],
templateUrl: './vehicle.component.html',
styleUrl: './vehicle.component.css'
})
export class VehicleComponent {
selectedVehicle = DUMMY_VEHICLES[randomIndex];
}
Variablen mit Scope private sind nicht im html-File verfügbar.
|
vehicle.component.html (String Interpolation)
<div>
<button>
<img src="icons/"/>
<span>{{ selectedVehicle.brand }}</span>
</button>
</div>
-
Sehen sie sich nun das Ergebnis an - Reloaden Sie die Seite.
vehicle.component.html (Property Binding)
<div>
<button>
<img
[src]="'icons/' + selectedVehicle.avatar"
[alt]="selectedVehicle.brand"
/>
<span>{{ selectedVehicle.brand }}</span>
</button>
</div>
-
<img [src]="someSrc">
bindet nicht das html-image tag sondern das darunterliegende HTMLImageElement DOM Objekt -
Dieser Artikel beschreibt den Unterschied zwischen Elementattributen und Properties
-
Sehen sie sich nun das Ergebnis an - Reloaden Sie die Seite.
4.1. Using Getters for Computed Values
vehicle.component.html
<div>
<button>
<img
[src]="imagePath"
[alt]="selectedVehicle.brand"
/>
<span>{{ selectedVehicle.brand }}</span>
</button>
</div>
vehicle.component.ts
// ommitted for brevity
@Component({
selector: 'app-vehicle',
imports: [],
templateUrl: './vehicle.component.html',
styleUrl: './vehicle.component.css'
})
export class VehicleComponent {
selectedVehicle = DUMMY_VEHICLES[randomIndex];
get imagePath() {
return 'icons/' + this.selectedVehicle.avatar;
}
}
4.2. Listening to Events With Event Binding

vehicle.component.html
<div>
<button (click)="onSelectUser()">
<img
[src]="imagePath"
[alt]="selectedVehicle.brand"
/>
<span>{{ selectedVehicle.brand }}</span>
</button>
</div>
vehicle.component.ts
// ommitted for brevity
@Component({
selector: 'app-vehicle',
imports: [],
templateUrl: './vehicle.component.html',
styleUrl: './vehicle.component.css'
})
export class VehicleComponent {
selectedVehicle = DUMMY_VEHICLES[randomIndex];
get imagePath() {
return 'icons/' + this.selectedVehicle.avatar;
}
onSelectUser() {
console.log('Clicked!');
}
}

4.3. Managing State & Changing Data
-
Nun wollen die UI mittels Button-Click andern — also den Zustand (State)
vehicle.component.ts
// ommitted for brevity
const randomIndex = Math.floor(Math.random() * DUMMY_VEHICLES.length);
@Component({
selector: 'app-vehicle',
imports: [],
templateUrl: './vehicle.component.html',
styleUrl: './vehicle.component.css'
})
export class VehicleComponent {
selectedVehicle = DUMMY_VEHICLES[randomIndex];
get imagePath() {
return 'icons/' + this.selectedVehicle.avatar;
}
onSelectUser() {
const randomIndex = Math.floor(Math.random() * DUMMY_VEHICLES.length);
this.selectedVehicle = DUMMY_VEHICLES[randomIndex];
}
}
4.5. Introducing Signals
-
vehicle.component.ts

vehicle.component.html
<div>
<button (click)="onSelectUser()">
<img
[src]="imagePath()" (1)
[alt]="selectedVehicle().brand" (1)
/>
<span>{{ selectedVehicle().brand }}</span> (1)
</button>
</div>
1 | Signals werden wie eine Methode mit runden Klammern angesprochen |
vehicle.component.ts