Angular-Leitfaden
So baust du Slider mit @reelkit/angular.
Die Komponente rk-reel
Die Komponente rk-reel kapselt den Slider-Controller aus dem Core. Sie ist standalone und nutzt ChangeDetectionStrategy.OnPush.
import { Component } from '@angular/core';
import { ReelComponent, ReelIndicatorComponent, RkReelItemDirective } from '@reelkit/angular';
@Component({
selector: 'app-root',
standalone: true,
imports: [ReelComponent, ReelIndicatorComponent, RkReelItemDirective],
template: `
<rk-reel
[count]="items.length"
style="width: 100%; height: 100dvh"
direction="vertical"
[enableWheel]="true"
(afterChange)="onAfterChange($event)"
>
<ng-template rkReelItem let-i let-size="size">
<div [style.width.px]="size[0]" [style.height.px]="size[1]"
[style.background]="items[i].color"
style="display:flex;flex-direction:column;align-items:center;justify-content:center;color:#fff">
<div style="font-size:1.5rem;font-weight:bold">{{ items[i].title }}</div>
<div style="font-size:0.875rem;opacity:0.8">{{ items[i].subtitle }}</div>
</div>
</ng-template>
<div style="position:absolute;right:12px;top:50%;transform:translateY(-50%);z-index:10">
<rk-reel-indicator direction="vertical" />
</div>
</rk-reel>
`,
})
export class AppComponent {
items = [
{ title: 'Virtualized', subtitle: 'Only 3 slides in DOM', color: '#6366f1' },
{ title: 'Touch First', subtitle: 'Native swipe gestures', color: '#8b5cf6' },
{ title: 'Zero Deps', subtitle: 'Tiny bundle size', color: '#7c3aed' },
{ title: 'Keyboard Nav', subtitle: 'Full a11y support', color: '#ec4899' },
{ title: 'SSR Ready', subtitle: 'Works everywhere', color: '#14b8a6' },
{ title: '60fps', subtitle: 'Smooth animations', color: '#f59e0b' },
];
onAfterChange(event: { index: number; indexInRange: number }) {
console.log('Current index:', event.index);
}
}Automatische Größe
Der Input size ist optional. Fehlt er, misst rk-reel seinen Container selbst über ResizeObserver und passt sich an das Layout aus dem CSS an. Die Größe des Containers muss vom Elternelement kommen (zum Beispiel Flex, Grid oder feste Maße im CSS).
<!-- Explicit size (fixed) -->
<rk-reel [count]="items.length" [size]="[400, 600]">
<ng-template rkReelItem let-i let-size="size"> ... </ng-template>
</rk-reel>
<!-- Auto-size (responsive — sized by CSS) -->
<rk-reel [count]="items.length" style="width: 100%; height: 100dvh">
<ng-template rkReelItem let-i let-size="size"> ... </ng-template>
</rk-reel>Template-Muster mit rkReelItem
Statt eines Render-Props wie in React nutzt Angular die strukturelle Direktive rkReelItem auf einem ng-template. Das ermöglicht die Virtualisierung: Nur sichtbare Slides werden erzeugt. Der Template-Kontext stellt drei Variablen bereit:
<ng-template rkReelItem let-i let-indexInRange="indexInRange" let-size="size">
<!--
$implicit (let-i) : number — absolute slide index (0 to count-1)
indexInRange : number — position in visible window (0, 1, or 2)
size : [number, number] — [width, height] of the container
-->
<app-slide [data]="items[i]"
[style.width.px]="size[0]"
[style.height.px]="size[1]" />
</ng-template>Navigation
Eingebaute Wege der Navigation:
- Touch/Wischen: Ziehen navigiert mit Schwung und Einrasten
- Tastatur: Pfeiltasten und Escape
- Mausrad: Einschalten mit
[enableWheel]="true" - Im Code: Über den Output
(apiReady)bekommst dunext(),prev()undgoTo()
import { Component } from '@angular/core';
import { ReelComponent, RkReelItemDirective, type ReelApi } from '@reelkit/angular';
@Component({
selector: 'app-root',
standalone: true,
imports: [ReelComponent, RkReelItemDirective],
template: `
<rk-reel
[count]="10"
[size]="[400, 600]"
(apiReady)="api = $event"
>
<ng-template rkReelItem let-i let-size="size">
<app-slide [index]="i" [size]="size" />
</ng-template>
</rk-reel>
<button (click)="api?.prev()">Prev</button>
<button (click)="api?.next()">Next</button>
<button (click)="api?.goTo(5)">Go to 5</button>
`,
})
export class AppComponent {
api: ReelApi | undefined;
}URL-State
createOverlayUrlState baut einen URL-State-Controller für ein Overlay und gibt ihn vollständig zurück. Du übergibst ihn dann einer *UrlOverlay-Komponente als Input [controller]. Rufe die Funktion in einem Injection-Kontext auf, also in einem Feld-Initialisierer; sie bindet sich sofort an und gibt sich über DestroyRef wieder frei. Die URL bestimmt, ob es offen ist, also öffnet sich ein gebundenes Overlay selbst, und ein Link ist der übliche Weg, es zu öffnen. Das erste Schreiben eines fehlenden Parameters legt einen Eintrag im Verlauf an, jedes weitere Schreiben ersetzt ihn, so dass Blättern den Zurück-Button nie verschüttet. Behalte den Controller, um value/position zu lesen und ihn im Code zu steuern: set(position) öffnet, set(null) schließt, und set ist dasselbe einfache Schreiben, das das Overlay beim Slide-Wechsel intern nutzt.
import { Component, signal } from '@angular/core';
import { RouterLink } from '@angular/router';
import { RkLightboxUrlOverlayComponent } from '@reelkit/angular-lightbox';
import { createOverlayUrlState, urlIndexKey } from '@reelkit/angular';
@Component({
imports: [RkLightboxUrlOverlayComponent, RouterLink],
template: `
@for (image of images(); track image.src; let i = $index) {
<a [routerLink]="[]" [queryParams]="{ photo: i }">
<img [src]="image.src" alt="" />
</a>
}
<rk-lightbox-url-overlay [controller]="photo" [items]="images()" />
`,
})
export class GalleryComponent {
protected readonly images = signal(photos);
// Attaches now, releases on destroy.
protected readonly photo = createOverlayUrlState({
param: 'photo',
...urlIndexKey(() => this.images().length),
});
}App mit Router: Übergib einen Adapter, der den Router nutzt. Sonst veraltet die Location des Routers, und seine nächste Navigation verwirft den Parameter:
import { createRouterUrlAdapter } from '@reelkit/angular/ng-router-url-adapter';
protected readonly photo = createOverlayUrlState({
param: 'photo',
adapter: createRouterUrlAdapter(),
...urlIndexKey(() => this.images().length),
});Das Options-Objekt nimmt param, codec und locator (alle drei Pflicht) sowie einen optionalen adapter. codec und locator sind ein zusammengehöriges Paar mit demselben Id. Eine einfache Galerie mit ?photo=3 setzt daher ...urlIndexKey(() => images().length) ein, das beide Hälften auf einmal liefert. urlIndexKey prüft den Index gegen die aktuelle Anzahl, die der Getter liefert. Ein veraltetes ?photo=99 wird also abgelehnt und verschwindet von selbst aus der URL, statt eine Slide zu öffnen, die es nie gab. Ein Feed mit Seiten oder eine Galerie mit Schlüsseln nach Identität liefert stattdessen ihr eigenes Paar aus codec und locator. Die vollständige Tabelle der Optionen steht in der Angular-API-Referenz.
ReelIndicator
Optionale Komponente, die Fortschrittsindikatoren wie bei Instagram anzeigt und die aktuelle Position im Slider zeigt. Steht sie innerhalb eines rk-reel, verbindet sie sich über das Injection-Token RK_REEL_CONTEXT automatisch mit den Werten count und active des Elternelements. Du musst nichts selbst verdrahten.
<!-- Auto-connect: count and active are inherited from parent rk-reel -->
<rk-reel [count]="10" [size]="[400, 600]">
<ng-template rkReelItem let-i let-size="size"> ... </ng-template>
<rk-reel-indicator direction="vertical" />
</rk-reel>
<!-- Manual usage: pass count and active explicitly (e.g. outside a rk-reel) -->
<rk-reel-indicator [count]="10" [active]="currentIndex" />Output apiReady — signalbasiertes Muster
Der Output (apiReady) feuert einmal, nachdem die Komponente eingehängt und vermessen ist. Er liefert ein ReelApi-Objekt, das du speichern und für die imperative Navigation nutzen kannst. Mit Angular-Signals für diese Referenz funktioniert das sauber mit der Change Detection OnPush.
import { Component, signal } from '@angular/core';
import { ReelComponent, RkReelItemDirective, type ReelApi } from '@reelkit/angular';
@Component({
selector: 'app-root',
standalone: true,
imports: [ReelComponent, RkReelItemDirective],
template: `
<rk-reel
[count]="items.length"
style="width: 100%; height: 100dvh"
direction="vertical"
[enableWheel]="true"
(apiReady)="reelApi.set($event)"
(afterChange)="currentIndex.set($event.index)"
>
<ng-template rkReelItem let-i let-size="size">
<div [style.width.px]="size[0]" [style.height.px]="size[1]">
{{ items[i].title }}
</div>
</ng-template>
</rk-reel>
<div style="position:absolute;bottom:16px;left:50%;transform:translateX(-50%)">
<button (click)="reelApi()?.prev()"
[disabled]="currentIndex() === 0">Prev</button>
<button (click)="reelApi()?.next()"
[disabled]="currentIndex() === items.length - 1">Next</button>
</div>
`,
})
export class AppComponent {
readonly items = [
{ title: 'Slide 1', color: '#6366f1' },
{ title: 'Slide 2', color: '#8b5cf6' },
{ title: 'Slide 3', color: '#ec4899' },
];
readonly reelApi = signal<ReelApi | undefined>(undefined);
readonly currentIndex = signal(0);
}Das Wichtigste
- Standalone-Komponente
Importiere
ReelComponent,RkReelItemDirectiveund optionalReelIndicatorComponentdirekt in das Arrayimportsdeiner Komponente - ng-template + rkReelItem
Das Gegenstück in Angular zum Prop
itemBuilderaus React — es ermöglicht die Virtualisierung - apiReady
Ein Output, der einmal mit der API für die imperative Navigation feuert — ohne Abfrage über ViewChild
- afterChange
Liefert
{ index, indexInRange }— so verfolgst du den aktuellen Index für die Oberfläche - Standardmäßig OnPush
Alle Komponenten nutzen
ChangeDetectionStrategy.OnPushund Angular-Signals für die beste Performance
Tipps zur Performance
- Halte die Slide-Templates schlank
Das Template
rkReelItemläuft für jede sichtbare Slide (meist 3 gleichzeitig). Vermeide darin aufwendige Berechnungen oder tief verschachtelte Strukturen. - Lade Daten nahe am Ende
Erkenne mit
(afterChange), wann sich der Nutzer dem Ende nähert, und lade den nächsten Schwung, bevor die Slides ausgehen — so entstehen endlos scrollende Feeds. - Nutze Signals für den imperativen Zustand
Speichere die Referenz auf
ReelApiund den aktuellen Index in Angular-Signals. So bekommst du feingranulare Reaktivität, ohne die ganze Komponente neu zu rendern. - Schalte das Mausrad auf scrollbaren Seiten ab
Setze
[enableWheel]="false", wenn der Slider in einem scrollbaren Layout steckt, damit er das Scrollen der Seite nicht abfängt.
Nächste Schritte
- Angular-API-Referenz - alle Inputs, Outputs und Methoden
- Reel Player - Videoplayer im Stil von TikTok/Reels
- Lightbox - Galerie für Bilder und Videos
- Stories Player - Stories-Ansicht im Stil von Instagram