Przejdź do treści
Frontend

Angular - Das vollstaendige Frontend-Framework fuer moderne Anwendungen

Veröffentlicht am:
·4 Min. Lesezeit·Autor: MDS Software Solutions Group
Angular - Das vollstaendige Frontend-Framework fuer moderne Anwendungen

Angular - Das vollstaendige Frontend-Framework fuer moderne Anwendungen

Angular ist eines der beliebtesten Frontend-Frameworks der Welt, entwickelt und gepflegt von Google. Seit der Veroeffentlichung von Angular 2 im Jahr 2016 hat das Framework eine enorme Entwicklung durchgemacht - von einer monolithischen, modulbasierten Architektur hin zu einem modernen Ansatz mit Standalone Components, Signals und Defer Blocks in den Versionen 17 und 18. In diesem Artikel werden wir alle wichtigen Aspekte von Angular untersuchen, von den Grundlagen bis hin zu fortgeschrittenen Techniken der Leistungsoptimierung.

Warum Angular im Jahr 2025?#

Angular hebt sich durch mehrere Schluesselmerkmale von der Konkurrenz ab:

  • Vollstaendigkeit - Angular liefert alles Out-of-the-Box: Routing, Formulare, HTTP-Client, Dependency Injection, Testing
  • TypeScript first - Angular wurde von Grund auf mit TypeScript entwickelt, was starke Typisierung und bessere Entwicklertools gewaehrleistet
  • Skalierbarkeit - funktioniert gleichermassen gut fuer kleine Projekte und grosse Unternehmensanwendungen
  • Oekosystem - Angular CLI, Angular Material, Angular Universal, Angular CDK
  • Stabilitaet - vorhersehbarer Release-Zyklus und langfristiger Support (LTS)
  • Neue APIs - Signals, Standalone Components und Defer Blocks revolutionieren die Developer Experience

Neuigkeiten in Angular 17/18#

Standalone Components#

Ab Angular 17 sind Standalone Components die Standardmethode zur Erstellung von Komponenten. Sie beseitigen die Notwendigkeit, NgModule fuer einfache Anwendungsfaelle zu deklarieren:

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterLink } from '@angular/router';

@Component({
  selector: 'app-hero',
  standalone: true,
  imports: [CommonModule, RouterLink],
  template: `
    <section class="hero">
      <h1>{{ title }}</h1>
      <p>{{ description }}</p>
      <a routerLink="/contact" class="cta-button">Kontakt aufnehmen</a>
    </section>
  `,
  styles: [`
    .hero {
      padding: 4rem 2rem;
      text-align: center;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
    }
    .cta-button {
      display: inline-block;
      padding: 12px 32px;
      background: white;
      color: #667eea;
      border-radius: 8px;
      text-decoration: none;
      font-weight: 600;
    }
  `]
})
export class HeroComponent {
  title = 'Willkommen in unserer Anwendung';
  description = 'Erstellt mit Angular 18 und Standalone Components';
}

Signals - Das neue Reaktivitaetssystem#

Signals sind ein neues Reaktivitaetsprimitiv in Angular, das eine granularere Aenderungsverfolgung und bessere Leistung im Vergleich zum traditionellen Zone.js-basierten Ansatz ermoeglicht:

import { Component, signal, computed, effect } from '@angular/core';

@Component({
  selector: 'app-counter',
  standalone: true,
  template: `
    <div class="counter">
      <h2>Zaehler: {{ count() }}</h2>
      <p>Verdoppelter Wert: {{ doubleCount() }}</p>
      <button (click)="increment()">+1</button>
      <button (click)="decrement()">-1</button>
      <button (click)="reset()">Zuruecksetzen</button>
    </div>
  `
})
export class CounterComponent {
  // Signal - reaktiver Wert
  count = signal(0);

  // Computed Signal - wird automatisch neu berechnet
  doubleCount = computed(() => this.count() * 2);

  constructor() {
    // Effect - reagiert auf Signal-Aenderungen
    effect(() => {
      console.log(`Aktueller Zaehlerwert: ${this.count()}`);
    });
  }

  increment() {
    this.count.update(value => value + 1);
  }

  decrement() {
    this.count.update(value => value - 1);
  }

  reset() {
    this.count.set(0);
  }
}

Signals unterstuetzen auch die Integration mit RxJS durch die Funktionen toSignal() und toObservable():

import { toSignal, toObservable } from '@angular/core/rxjs-interop';
import { interval } from 'rxjs';
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-clock',
  standalone: true,
  template: `<p>Uhrzeit: {{ time() }}</p>`
})
export class ClockComponent {
  // Observable in Signal umwandeln
  time = toSignal(
    interval(1000).pipe(
      map(() => new Date().toLocaleTimeString())
    ),
    { initialValue: new Date().toLocaleTimeString() }
  );
}

Defer Blocks - Deklaratives Lazy Loading von Komponenten#

Defer Blocks bieten eine deklarative Moeglichkeit, Teile eines Templates lazy zu laden, was die initiale Ladezeit der Seite erheblich verbessert:

@Component({
  selector: 'app-dashboard',
  standalone: true,
  imports: [HeavyChartComponent, DataTableComponent],
  template: `
    <h1>Dashboard</h1>

    <!-- Diagramm nach 2 Sekunden laden -->
    @defer (on timer(2s)) {
      <app-heavy-chart [data]="chartData" />
    } @placeholder {
      <div class="skeleton">Diagramm wird geladen...</div>
    } @loading (minimum 500ms) {
      <div class="spinner">Komponente wird geladen...</div>
    } @error {
      <p>Diagramm konnte nicht geladen werden</p>
    }

    <!-- Tabelle laden, wenn im Viewport sichtbar -->
    @defer (on viewport) {
      <app-data-table [data]="tableData" />
    } @placeholder {
      <div class="skeleton-table">Scrollen Sie, um die Tabelle zu sehen</div>
    }
  `
})
export class DashboardComponent {
  chartData = signal<ChartData[]>([]);
  tableData = signal<TableRow[]>([]);
}

Komponenten und Templates#

Angular verwendet TypeScript-Dekoratoren zur Definition von Komponenten. Jede Komponente besteht aus einer TypeScript-Klasse, einem HTML-Template und optionalen Styles:

import { Component, Input, Output, EventEmitter } from '@angular/core';
import { CommonModule } from '@angular/common';

interface Product {
  id: number;
  name: string;
  price: number;
  description: string;
  inStock: boolean;
}

@Component({
  selector: 'app-product-card',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="product-card" [class.out-of-stock]="!product.inStock">
      <h3>{{ product.name }}</h3>
      <p class="price">{{ product.price | currency:'EUR':'symbol':'1.2-2' }}</p>
      <p>{{ product.description }}</p>

      @if (product.inStock) {
        <button (click)="onAddToCart()" class="btn-primary">
          In den Warenkorb
        </button>
      } @else {
        <span class="badge-unavailable">Nicht verfuegbar</span>
      }
    </div>
  `
})
export class ProductCardComponent {
  @Input({ required: true }) product!: Product;
  @Output() addToCart = new EventEmitter<Product>();

  onAddToCart() {
    this.addToCart.emit(this.product);
  }
}

Neue Control-Flow-Syntax#

Angular 17 fuehrte eine neue integrierte Control-Flow-Syntax ein, die strukturelle Direktiven ersetzt:

<!-- Neue @if-Syntax -->
@if (users().length > 0) {
  <ul>
    @for (user of users(); track user.id) {
      <li>{{ user.name }} - {{ user.email }}</li>
    } @empty {
      <li>Keine Benutzer vorhanden</li>
    }
  </ul>
} @else {
  <p>Daten werden geladen...</p>
}

<!-- Neue @switch-Syntax -->
@switch (status()) {
  @case ('active') {
    <span class="badge-success">Aktiv</span>
  }
  @case ('inactive') {
    <span class="badge-warning">Inaktiv</span>
  }
  @default {
    <span class="badge-info">Unbekannt</span>
  }
}

Dependency Injection#

Das Dependency-Injection-System (DI) in Angular ist eine seiner wichtigsten Saeulen. Es ermoeglicht lose gekoppelte Komponenten und einfacheres Testen:

import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
import { map, tap } from 'rxjs/operators';

interface User {
  id: number;
  name: string;
  email: string;
  role: string;
}

@Injectable({
  providedIn: 'root'  // Anwendungsweiter Singleton
})
export class UserService {
  private http = inject(HttpClient);
  private apiUrl = '/api/users';

  private usersSubject = new BehaviorSubject<User[]>([]);
  users$ = this.usersSubject.asObservable();

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>(this.apiUrl).pipe(
      tap(users => this.usersSubject.next(users))
    );
  }

  getUserById(id: number): Observable<User> {
    return this.http.get<User>(`${this.apiUrl}/${id}`);
  }

  createUser(user: Omit<User, 'id'>): Observable<User> {
    return this.http.post<User>(this.apiUrl, user).pipe(
      tap(newUser => {
        const current = this.usersSubject.getValue();
        this.usersSubject.next([...current, newUser]);
      })
    );
  }

  deleteUser(id: number): Observable<void> {
    return this.http.delete<void>(`${this.apiUrl}/${id}`).pipe(
      tap(() => {
        const current = this.usersSubject.getValue();
        this.usersSubject.next(current.filter(u => u.id !== id));
      })
    );
  }
}

Die inject()-Funktion ist die moderne Alternative zur Constructor-Injection:

@Component({
  selector: 'app-user-list',
  standalone: true,
  imports: [CommonModule],
  template: `
    @for (user of users(); track user.id) {
      <div class="user-card">
        <h3>{{ user.name }}</h3>
        <p>{{ user.email }}</p>
      </div>
    }
  `
})
export class UserListComponent {
  private userService = inject(UserService);
  users = toSignal(this.userService.users$, { initialValue: [] });
}

RxJS und Observables#

RxJS ist ein integraler Bestandteil von Angular. Observables werden in HTTP-Requests, reaktiven Formularen, dem Router und an vielen anderen Stellen verwendet:

import { Component, OnInit, OnDestroy, inject } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import {
  Subject, debounceTime, distinctUntilChanged,
  switchMap, takeUntil, catchError, of
} from 'rxjs';

@Component({
  selector: 'app-search',
  standalone: true,
  imports: [ReactiveFormsModule, CommonModule],
  template: `
    <div class="search-container">
      <input
        [formControl]="searchControl"
        placeholder="Produkte suchen..."
        class="search-input"
      />

      @if (isLoading()) {
        <div class="loading-spinner"></div>
      }

      <div class="results">
        @for (result of results(); track result.id) {
          <div class="result-item">
            <h4>{{ result.name }}</h4>
            <p>{{ result.description }}</p>
          </div>
        }
      </div>
    </div>
  `
})
export class SearchComponent implements OnInit, OnDestroy {
  private searchService = inject(SearchService);
  private destroy$ = new Subject<void>();

  searchControl = new FormControl('');
  results = signal<SearchResult[]>([]);
  isLoading = signal(false);

  ngOnInit() {
    this.searchControl.valueChanges.pipe(
      debounceTime(300),
      distinctUntilChanged(),
      tap(() => this.isLoading.set(true)),
      switchMap(query =>
        this.searchService.search(query ?? '').pipe(
          catchError(() => of([]))
        )
      ),
      takeUntil(this.destroy$)
    ).subscribe(results => {
      this.results.set(results);
      this.isLoading.set(false);
    });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

Angular Router#

Der Angular Router ist ein leistungsstarkes Navigationssystem mit Unterstuetzung fuer Lazy Loading, Guards, Resolver und verschachtelte Routen:

import { Routes } from '@angular/router';
import { inject } from '@angular/core';
import { AuthService } from './services/auth.service';

export const routes: Routes = [
  {
    path: '',
    loadComponent: () =>
      import('./pages/home/home.component')
        .then(m => m.HomeComponent),
    title: 'Startseite'
  },
  {
    path: 'products',
    loadChildren: () =>
      import('./pages/products/product.routes')
        .then(m => m.PRODUCT_ROUTES),
    title: 'Produkte'
  },
  {
    path: 'admin',
    loadChildren: () =>
      import('./pages/admin/admin.routes')
        .then(m => m.ADMIN_ROUTES),
    canActivate: [() => inject(AuthService).isAuthenticated()],
    title: 'Verwaltungsbereich'
  },
  {
    path: '**',
    loadComponent: () =>
      import('./pages/not-found/not-found.component')
        .then(m => m.NotFoundComponent),
    title: 'Seite nicht gefunden'
  }
];

Beispiel fuer verschachtelte Routen mit Resolvern:

// product.routes.ts
export const PRODUCT_ROUTES: Routes = [
  {
    path: '',
    loadComponent: () =>
      import('./product-list.component')
        .then(m => m.ProductListComponent)
  },
  {
    path: ':id',
    loadComponent: () =>
      import('./product-detail.component')
        .then(m => m.ProductDetailComponent),
    resolve: {
      product: (route: ActivatedRouteSnapshot) => {
        const productService = inject(ProductService);
        return productService.getById(Number(route.paramMap.get('id')));
      }
    }
  }
];

Reaktive Formulare#

Reaktive Formulare in Angular bieten volle Kontrolle ueber Validierung und Formularstatus:

import { Component, inject } from '@angular/core';
import {
  FormBuilder, FormGroup, Validators,
  ReactiveFormsModule, AbstractControl
} from '@angular/forms';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-registration',
  standalone: true,
  imports: [ReactiveFormsModule, CommonModule],
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <div class="form-group">
        <label for="name">Vollstaendiger Name</label>
        <input id="name" formControlName="name" />
        @if (form.get('name')?.hasError('required') &&
             form.get('name')?.touched) {
          <span class="error">Name ist erforderlich</span>
        }
      </div>

      <div class="form-group">
        <label for="email">E-Mail</label>
        <input id="email" type="email" formControlName="email" />
        @if (form.get('email')?.hasError('email') &&
             form.get('email')?.touched) {
          <span class="error">Bitte geben Sie eine gueltige E-Mail-Adresse ein</span>
        }
      </div>

      <div class="form-group">
        <label for="password">Passwort</label>
        <input id="password" type="password" formControlName="password" />
        @if (form.get('password')?.hasError('minlength') &&
             form.get('password')?.touched) {
          <span class="error">Das Passwort muss mindestens 8 Zeichen lang sein</span>
        }
      </div>

      <div class="form-group">
        <label for="confirmPassword">Passwort bestaetigen</label>
        <input id="confirmPassword" type="password"
               formControlName="confirmPassword" />
        @if (form.hasError('passwordsMismatch') &&
             form.get('confirmPassword')?.touched) {
          <span class="error">Passwoerter muessen uebereinstimmen</span>
        }
      </div>

      <button type="submit" [disabled]="form.invalid">
        Registrieren
      </button>
    </form>
  `
})
export class RegistrationComponent {
  private fb = inject(FormBuilder);

  form: FormGroup = this.fb.group({
    name: ['', [Validators.required, Validators.minLength(2)]],
    email: ['', [Validators.required, Validators.email]],
    password: ['', [Validators.required, Validators.minLength(8)]],
    confirmPassword: ['', Validators.required]
  }, {
    validators: this.passwordMatchValidator
  });

  passwordMatchValidator(control: AbstractControl) {
    const password = control.get('password')?.value;
    const confirmPassword = control.get('confirmPassword')?.value;
    return password === confirmPassword ? null : { passwordsMismatch: true };
  }

  onSubmit() {
    if (this.form.valid) {
      console.log('Formular gesendet:', this.form.value);
    }
  }
}

HttpClient und API-Kommunikation#

Der Angular HttpClient ist ein leistungsstarkes Werkzeug fuer die API-Kommunikation, das Observables zurueckgibt und Interceptors unterstuetzt:

// auth.interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from './auth.service';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const authService = inject(AuthService);
  const token = authService.getToken();

  if (token) {
    const cloned = req.clone({
      headers: req.headers.set('Authorization', `Bearer ${token}`)
    });
    return next(cloned);
  }

  return next(req);
};

// error.interceptor.ts
export const errorInterceptor: HttpInterceptorFn = (req, next) => {
  return next(req).pipe(
    catchError((error: HttpErrorResponse) => {
      if (error.status === 401) {
        inject(AuthService).logout();
        inject(Router).navigate(['/login']);
      }
      return throwError(() => error);
    })
  );
};

// app.config.ts
export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient(
      withInterceptors([authInterceptor, errorInterceptor])
    )
  ]
};

Angular CLI#

Angular CLI ist ein unverzichtbares Werkzeug fuer die Arbeit mit Angular. Es beschleunigt die Projekterstellung, Komponentengenerierung, Service-Scaffolding und vieles mehr:

# Neues Projekt erstellen
ng new my-app --style=scss --routing --strict

# Komponenten und Services generieren
ng generate component features/dashboard --standalone
ng generate service services/api
ng generate pipe pipes/currency-format
ng generate guard guards/auth --functional
ng generate interceptor interceptors/auth --functional

# Erstellen und ausfuehren
ng serve --open
ng build --configuration=production
ng test
ng e2e

# Angular aktualisieren
ng update @angular/core @angular/cli

Testen mit Jasmine/Karma und Jest#

Angular hat eingebaute Testunterstuetzung. Traditionell verwendet es Jasmine und Karma, aber immer mehr Teams wechseln zu Jest:

// user.service.spec.ts
import { TestBed } from '@angular/core/testing';
import {
  HttpClientTestingModule,
  HttpTestingController
} from '@angular/common/http/testing';
import { UserService } from './user.service';

describe('UserService', () => {
  let service: UserService;
  let httpMock: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [UserService]
    });

    service = TestBed.inject(UserService);
    httpMock = TestBed.inject(HttpTestingController);
  });

  afterEach(() => {
    httpMock.verify();
  });

  it('sollte die Benutzerliste abrufen', () => {
    const mockUsers = [
      { id: 1, name: 'Max Mueller', email: 'max@example.com', role: 'admin' },
      { id: 2, name: 'Anna Schmidt', email: 'anna@example.com', role: 'user' }
    ];

    service.getUsers().subscribe(users => {
      expect(users.length).toBe(2);
      expect(users[0].name).toBe('Max Mueller');
    });

    const req = httpMock.expectOne('/api/users');
    expect(req.request.method).toBe('GET');
    req.flush(mockUsers);
  });

  it('sollte einen neuen Benutzer erstellen', () => {
    const newUser = { name: 'Peter Weber', email: 'peter@example.com', role: 'user' };
    const createdUser = { id: 3, ...newUser };

    service.createUser(newUser).subscribe(user => {
      expect(user.id).toBe(3);
      expect(user.name).toBe('Peter Weber');
    });

    const req = httpMock.expectOne('/api/users');
    expect(req.request.method).toBe('POST');
    req.flush(createdUser);
  });
});

Komponententests:

// counter.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CounterComponent } from './counter.component';

describe('CounterComponent', () => {
  let component: CounterComponent;
  let fixture: ComponentFixture<CounterComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [CounterComponent]
    }).compileComponents();

    fixture = TestBed.createComponent(CounterComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('sollte mit dem Wert 0 starten', () => {
    expect(component.count()).toBe(0);
  });

  it('sollte den Wert beim Klick auf +1 erhoehen', () => {
    component.increment();
    expect(component.count()).toBe(1);
    expect(component.doubleCount()).toBe(2);
  });

  it('sollte den Wert zuruecksetzen', () => {
    component.increment();
    component.increment();
    component.reset();
    expect(component.count()).toBe(0);
  });
});

Angular Material#

Angular Material ist die offizielle UI-Komponentenbibliothek, die nach den Material-Design-Richtlinien erstellt wurde:

import { Component, inject } from '@angular/core';
import { MatTableModule, MatTableDataSource } from '@angular/material/table';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';
import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';

@Component({
  selector: 'app-user-table',
  standalone: true,
  imports: [
    MatTableModule, MatPaginatorModule, MatSortModule,
    MatInputModule, MatFormFieldModule, MatButtonModule,
    MatIconModule, MatDialogModule
  ],
  template: `
    <mat-form-field appearance="outline" class="filter-field">
      <mat-label>Filter</mat-label>
      <input matInput (keyup)="applyFilter($event)" placeholder="Suchen..." />
      <mat-icon matSuffix>search</mat-icon>
    </mat-form-field>

    <table mat-table [dataSource]="dataSource" matSort>
      <ng-container matColumnDef="name">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
        <td mat-cell *matCellDef="let user">{{ user.name }}</td>
      </ng-container>

      <ng-container matColumnDef="email">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>E-Mail</th>
        <td mat-cell *matCellDef="let user">{{ user.email }}</td>
      </ng-container>

      <ng-container matColumnDef="actions">
        <th mat-header-cell *matHeaderCellDef>Aktionen</th>
        <td mat-cell *matCellDef="let user">
          <button mat-icon-button color="primary" (click)="editUser(user)">
            <mat-icon>edit</mat-icon>
          </button>
          <button mat-icon-button color="warn" (click)="deleteUser(user)">
            <mat-icon>delete</mat-icon>
          </button>
        </td>
      </ng-container>

      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
    </table>

    <mat-paginator [pageSizeOptions]="[5, 10, 25]" showFirstLastButtons />
  `
})
export class UserTableComponent {
  private dialog = inject(MatDialog);
  displayedColumns = ['name', 'email', 'actions'];
  dataSource = new MatTableDataSource<User>();

  applyFilter(event: Event) {
    const filterValue = (event.target as HTMLInputElement).value;
    this.dataSource.filter = filterValue.trim().toLowerCase();
  }

  editUser(user: User) {
    // Benutzer bearbeiten
  }

  deleteUser(user: User) {
    // Benutzer loeschen
  }
}

SSR mit Angular Universal#

Server-Side Rendering (SSR) in Angular verbessert SEO und die Ladezeit beim ersten Aufruf. In Angular 17+ ist die SSR-Konfiguration deutlich einfacher:

# SSR zu einem bestehenden Projekt hinzufuegen
ng add @angular/ssr
// app.config.server.ts
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { appConfig } from './app.config';

const serverConfig: ApplicationConfig = {
  providers: [
    provideServerRendering()
  ]
};

export const config = mergeApplicationConfig(appConfig, serverConfig);

Angular 17+ unterstuetzt auch clientseitige Hydration, die das Flackern der Seite eliminiert:

// app.config.ts
import { provideClientHydration } from '@angular/platform-browser';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient(withFetch()),
    provideClientHydration()
  ]
};

Leistungsoptimierung#

1. Change-Detection-Strategie#

Verwenden Sie OnPush Change Detection in Kombination mit Signals:

@Component({
  selector: 'app-product-list',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    @for (product of products(); track product.id) {
      <app-product-card [product]="product" />
    }
  `
})
export class ProductListComponent {
  products = input.required<Product[]>();
}

2. Lazy Loading und Preloading#

// Preloading-Konfiguration
export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(
      routes,
      withPreloading(PreloadAllModules),
      withComponentInputBinding(),
      withViewTransitions()
    )
  ]
};

3. TrackBy in Schleifen#

Die neue @for-Syntax erfordert die Verwendung von track, was eine optimale Rendering-Leistung fuer Listen gewaehrleistet:

@for (item of items(); track item.id) {
  <app-item [data]="item" />
}

4. Bildoptimierung#

Angular stellt die NgOptimizedImage-Direktive zur Bildoptimierung bereit:

import { NgOptimizedImage } from '@angular/common';

@Component({
  standalone: true,
  imports: [NgOptimizedImage],
  template: `
    <img ngSrc="/images/hero.webp"
         width="800"
         height="400"
         priority
         placeholder />
  `
})
export class HeroImageComponent {}

Zusammenfassung#

Angular in den Versionen 17 und 18 ist ein modernes, ausgereiftes Framework, das die besten Entwurfsmuster mit modernsten APIs verbindet. Standalone Components, Signals und Defer Blocks vereinfachen die Anwendungsentwicklung erheblich, waehrend das reichhaltige Oekosystem an Werkzeugen - von Angular CLI bis Angular Material - die Teamproduktivitaet in jeder Projektphase beschleunigt.

Die wesentlichen Vorteile von Angular sind die Vollstaendigkeit der Loesung, starke Typisierung durch TypeScript, ein effizientes Dependency-Injection-System und integrierte Unterstuetzung fuer Testing, SSR und Leistungsoptimierung. Das Framework eignet sich hervorragend fuer grosse Unternehmensanwendungen, SaaS-Plattformen und ueberall dort, wo Skalierbarkeit und Wartbarkeit gefordert sind.

Brauchen Sie Hilfe bei Ihrem Angular-Projekt?#

Bei MDS Software Solutions Group sind wir auf die Entwicklung moderner Webanwendungen mit Angular spezialisiert. Wir bieten:

  • Entwicklung von Angular-Anwendungen von Grund auf
  • Migration von aelteren Angular-Versionen und AngularJS
  • Leistungsoptimierung bestehender Anwendungen
  • SSR- und Prerendering-Implementierung
  • Integration mit .NET, Node.js und anderen Backends

Kontaktieren Sie uns, um Ihr Projekt zu besprechen!

Autor
MDS Software Solutions Group

Team von Programmierexperten, die sich auf moderne Webtechnologien spezialisiert haben.

Angular - Das vollstaendige Frontend-Framework fuer moderne Anwendungen | MDS Software Solutions Group | MDS Software Solutions Group