Drei Pipes, die auf den ersten Blick trivial aussehen — und doch viele Fallstricke haben: uppercase, lowercase und titlecase. Sie kommen ohne Argumente aus, sind alle pure und alle aus @angular/common. Wo die ersten beiden simple Wrapper um native String-Methoden sind, hat die TitleCasePipe ein Eigenleben mit überraschenden Ergebnissen für Eigennamen, Akronyme und nicht-westliche Sprachen. Dieser Artikel zeigt, wo die Pipes glänzen — und wo CSS oder eine eigene Pipe die bessere Wahl ist.
Die drei Case-Pipes
Aus @angular/common stammen drei Pipes, die ausschließlich die Schreibweise eines Strings ändern:
UpperCasePipe— Template:| uppercase— alles GROSS.LowerCasePipe— Template:| lowercase— alles klein.TitleCasePipe— Template:| titlecase— Erster Buchstabe Jedes Wortes Gross.
Alle drei sind pure (Caching), erwarten keine Argumente, akzeptieren string | null | undefined und geben bei null/undefined ebenfalls null zurück. Im Standalone-Setup importierst du sie einzeln in das imports-Array deiner Komponente — CommonModule ist nicht nötig.
import { Component } from '@angular/core';
import { UpperCasePipe, LowerCasePipe, TitleCasePipe } from '@angular/common';
@Component({
selector: 'app-cases',
imports: [UpperCasePipe, LowerCasePipe, TitleCasePipe],
template: `
<p>{{ 'hallo welt' | uppercase }}</p> <!-- HALLO WELT -->
<p>{{ 'HALLO WELT' | lowercase }}</p> <!-- hallo welt -->
<p>{{ 'hallo welt' | titlecase }}</p> <!-- Hallo Welt -->
`
})
export class CasesComponent {}UpperCasePipe
Die UpperCasePipe ist intern ein dünner Wrapper um String.prototype.toUpperCase(). Sie nimmt keine Locale-Information entgegen und arbeitet rein UTF-16-basiert — sprachspezifische Casing-Regeln (siehe Türkisch weiter unten) werden ignoriert.
<p>{{ 'angular' | uppercase }}</p> <!-- ANGULAR -->
<p>{{ 'straße' | uppercase }}</p> <!-- STRAßE (NICHT STRASSE!) -->
<p>{{ 'café résumé' | uppercase }}</p> <!-- CAFÉ RÉSUMÉ -->
<p>{{ 'müller-lüdenscheidt' | uppercase }}</p><!-- MÜLLER-LÜDENSCHEIDT -->Beachte das ß — die ECMAScript-Spezifikation macht aus 'ß'.toUpperCase() weiterhin 'ß', nicht das offizielle Großbuchstaben-Eszett ẞ und auch nicht SS. Wer das braucht, muss eine eigene Pipe mit toLocaleUpperCase('de') schreiben.
LowerCasePipe
Symmetrisch dazu wrappt LowerCasePipe die native String.prototype.toLowerCase(). Auch hier: keine Locale, keine Argumente, kein Sonderfall. Bei null/undefined Rückgabe null.
<p>{{ 'HELLO WORLD' | lowercase }}</p> <!-- hello world -->
<p>{{ 'iOS Push' | lowercase }}</p> <!-- ios push -->
<p>{{ null | lowercase }}</p> <!-- (leer) -->Typischer Anwendungsfall: Case-insensitive Suche. Statt im TypeScript-Code mit String.prototype.toLowerCase() zu hantieren, kann man Eingabewerte direkt im Template normalisieren.
TitleCasePipe — was tut sie genau?
Hier wird es spannend. Die TitleCasePipe macht nicht echtes „Title Case“ im journalistischen Sinn (Bindewörter klein), sondern einen rein mechanischen Schritt:
- Der String wird an Whitespace (Space, Tab, Linefeed) in „Wörter“ geschnitten.
- Vom ersten Zeichen jedes Wortes wird
toUpperCase()gerufen. - Vom Rest des Wortes wird
toLowerCase()gerufen.
<p>{{ 'hello world' | titlecase }}</p> <!-- Hello World -->
<p>{{ 'this is a TEST' | titlecase }}</p> <!-- This Is A Test -->
<p>{{ 'müller meier' | titlecase }}</p> <!-- Müller Meier -->Wichtig: Bindestrich, Komma, Pipe-Zeichen trennen NICHT — nur Whitespace. Das verursacht im Alltag die meisten Überraschungen.
TitleCasePipe-Pitfalls
Die mechanische Logik führt bei Eigennamen und Akronymen schnell zu falschen Ergebnissen:
| Eingabe | Ausgabe | titlecase | Korrekt wäre |
| --- | --- | --- |
| iPhone | Iphone | iPhone |
| McDonald | Mcdonald | McDonald |
| IBM | Ibm | IBM |
| NASA-Mission | Nasa-mission | NASA-Mission |
| it's non-trivial | It's Non-trivial | It's Non-Trivial |
| foo-vs-bar | Foo-vs-bar | Foo-vs-Bar |
| one,two,three | One,two,three | One,Two,Three |
| true|false | True|false | (je nach Kontext) |
Tipp für eine eigene Pipe: Eine simple Map mit Sonderfällen ({ 'iphone': 'iPhone', 'ibm': 'IBM' }) reicht oft schon, um 90 % der Fehler abzufangen — Lookup vor der mechanischen Title-Case-Logik.
Locale-Verhalten der toUpperCase/toLowerCase
JavaScript kennt zwei Arten der Case-Konvertierung:
toUpperCase()/toLowerCase()— Locale-unabhängig, UTF-16-basiert.toLocaleUpperCase(locale)/toLocaleLowerCase(locale)— sprachsensitiv.
Die Angular-Pipes nutzen ausschließlich die erste, locale-unabhängige Variante. Für die meisten westeuropäischen Sprachen ist das egal — für Türkisch (tr-TR) aber nicht:
// Ohne Locale (Pipe-Verhalten)
'i'.toUpperCase(); // 'I' — falsch für Türkisch
'I'.toLowerCase(); // 'i' — falsch für Türkisch
// Mit Türkisch-Locale (was Angular NICHT macht)
'i'.toLocaleUpperCase('tr-TR'); // 'İ' — punktiertes I
'I'.toLocaleLowerCase('tr-TR'); // 'ı' — punktloses iKonsequenz: Wer eine türkische App baut und auf korrekte Casing-Regeln angewiesen ist, kann die Standard-Pipes nicht verwenden. Lösung: eine kleine Custom-Pipe, die toLocaleUpperCase(LOCALE_ID) aufruft und die LOCALE_ID per inject() zieht.
Praxis: Suchfeld mit Case-Insensitive-Filter
Ein klassischer Einsatzort für LowerCasePipe: ein Filter über eine Liste, der unabhängig von Groß-/Kleinschreibung arbeiten soll. Statt im Code zu normalisieren, geht das deklarativ:
import { Component, signal, computed } from '@angular/core';
import { LowerCasePipe } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-search-filter',
imports: [LowerCasePipe, FormsModule],
template: `
<input [(ngModel)]="query" placeholder="Suche…" />
<p>Du suchst: {{ query() | lowercase }}</p>
<ul>
@for (name of filtered(); track name) {
<li>{{ name }}</li>
}
</ul>
`
})
export class SearchFilterComponent {
query = signal('');
names = signal(['Angular', 'Astro', 'React', 'Vue', 'Svelte']);
filtered = computed(() => {
const q = this.query().toLowerCase();
return this.names().filter(n => n.toLowerCase().includes(q));
});
}Im Template normalisiert die Pipe den Anzeige-String, im computed() macht die Logik die eigentliche Filterung. Beides bleibt sauber getrennt.
Wann Pipe vs. CSS text-transform?
Für reine UI-Anzeige ist CSS fast immer die bessere Wahl. CSS text-transform: uppercase arbeitet auf der Render-Ebene, ändert nicht den DOM-String und respektiert (in modernen Browsern) sogar die Sprache via lang-Attribut.
| Aspekt | Pipe (| uppercase) | CSS (text-transform: uppercase) | Intl-API |
| --- | --- | --- | --- |
| Verändert DOM-String | Ja | Nein | Ja (im JS-Code) |
| Render-Cost | gering, aber vorhanden | praktisch null | gering |
| Locale-Sensitiv | nein | ja (via lang) | ja |
| Aria-Label-tauglich | ja | nein (nur visuell) | ja |
| Kopieren liefert | Großschreibung | Originalschreibung | Großschreibung |
| Wartbarkeit | logisch im Template | rein im CSS | logisch im Code |
Faustregel: Wenn der Wert nur visuell groß sein soll (Überschriften, Buttons), nimm CSS. Wenn der Wert auch außerhalb der Anzeige (z. B. in einem aria-label, einer Suchabfrage oder einem Tooltip) groß sein muss, nimm die Pipe.
Praxis-Notizen zu den Case-Pipes
TitleCasePipe ist nicht smart
Sie macht „erstes Zeichen jedes Wortes groß, Rest klein“ — keine echten Title-Case-Regeln. Bindewörter (and, or, the) werden nicht klein gehalten, Akronyme nicht erhalten. Für redaktionelle Headlines lieber eine eigene Pipe oder den korrekten Wert direkt aus dem CMS pflegen.
Case-Pipes nutzen NICHT toLocaleUpperCase
Sprachspezifische Casing-Regeln werden ignoriert. Türkisch (i/İ und ı/I), Litauisch und einige andere Locales bekommen falsche Ergebnisse. Wer das braucht, schreibt eine kurze Custom-Pipe, die LOCALE_ID injiziert und toLocaleUpperCase(locale) aufruft.
CSS text-transform ist für UI meist besser
Es verändert den DOM-String nicht, kostet keinen Re-Render-Aufwand und kann in modernen Browsern via lang-Attribut sogar Locale-sensitiv arbeiten. Pipes lohnen sich nur, wenn der formatierte String auch außerhalb des sichtbaren Textes (z. B. als aria-label) gebraucht wird.
Pure pipes — Caching greift bei identischen Inputs
Alle drei Pipes sind pure. In einer Schleife mit @for und vielen identischen Strings wird transform() nur einmal pro Referenz aufgerufen. Bei OnPush und Signals ist das praktisch kostenlos — Sorgen über Performance sind unbegründet.
ASCII-Akronym-Falle
‘NASA’ | titlecase wird zu ‘Nasa’, ‘CEO’ | titlecase zu ‘Ceo’. Wer Akronyme in benutzergenerierten Daten hat, sollte vor dem Pipe-Aufruf eine Whitelist anwenden oder Title-Case komplett vermeiden.
Standalone-Import — einzeln, nicht über CommonModule
Im modernen Standalone-Setup importierst du die Pipes einzeln: imports: [UpperCasePipe, LowerCasePipe, TitleCasePipe]. Das spart Bundle-Größe und macht Abhängigkeiten explizit — der frühere CommonModule-Import ist nicht mehr nötig.
null und undefined sind safe
Alle drei Pipes geben bei null oder undefined kommentarlos null zurück, was im Template als leerer String erscheint. Du brauchst kein @if davor — die Pipe macht den Schutz selbst.
Weiterführende Ressourcen
Externe Quellen
- UpperCasePipe API – Angular.dev
- LowerCasePipe API – Angular.dev
- TitleCasePipe API – Angular.dev
- String.prototype.toLocaleUpperCase – MDN