Guía de Angular
Aprende a crear sliders con @reelkit/angular.
Componente rk-reel
El componente rk-reel envuelve el controlador del slider del core. Es standalone y usa 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);
}
}Tamaño automático
El input size es opcional. Sin él, rk-reel mide su contenedor con ResizeObserver y se adapta al diseño que marque el CSS. El tamaño del contenedor lo tiene que dar su padre (por ejemplo, flex, grid o dimensiones explícitas en 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>Patrón de plantilla rkReelItem
En lugar de la render prop de React, Angular usa la directiva estructural rkReelItem sobre un ng-template. Esto hace posible la virtualización: solo se instancian los slides visibles. El contexto de la plantilla ofrece tres variables:
<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>Navegación
Métodos de navegación incluidos:
- Táctil / deslizar: arrastra para navegar con inercia y ajuste al slide
- Teclado: teclas de flecha y Escape
- Rueda del ratón: se activa con
[enableWheel]="true" - Por código: usa el output
(apiReady)para obtenernext(),prev(),goTo()
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;
}Estado en la URL
createOverlayUrlState crea un controlador de estado en la URL para un overlay y lo devuelve entero; después se lo pasas a un componente *UrlOverlay en su input [controller]. Llámalo en un contexto de inyección, como el inicializador de un campo; se conecta al momento y se libera con DestroyRef. La URL es dueña del estado abierto, así que un overlay vinculado se abre solo y lo habitual es abrirlo con un enlace. La primera escritura de un parámetro ausente añade una entrada al historial y las siguientes la sustituyen, así que pasar slides nunca entierra el botón de volver. Guarda el controlador para leer value/position y manejarlo por código: set(position) abre, set(null) cierra, y set es la misma escritura de bajo nivel que usa el overlay internamente al cambiar de slide.
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),
});
}En una aplicación con router, pasa un adaptador basado en el Router; si no, la ubicación del propio Router queda desfasada y su siguiente navegación pierde el parámetro:
import { createRouterUrlAdapter } from '@reelkit/angular/ng-router-url-adapter';
protected readonly photo = createOverlayUrlState({
param: 'photo',
adapter: createRouterUrlAdapter(),
...urlIndexKey(() => this.images().length),
});El objeto de opciones recibe param, codec y locator (los tres obligatorios), más un adapter opcional. codec y locator son un par a juego que comparte el mismo Id, así que una galería simple con ?photo=3 usa ...urlIndexKey(() => images().length), que devuelve las dos mitades a la vez. urlIndexKey limita el índice con el recuento actual que devuelve el getter, así que un ?photo=99 desfasado se rechaza y se limpia solo de la URL en lugar de abrir un slide que nunca se nombró. Un feed paginado o una galería por identidad aporta su propio par de codec + locator. La tabla completa de opciones está en la referencia de la API de Angular.
ReelIndicator
Componente opcional que muestra indicadores de progreso al estilo de Instagram con la posición actual en el slider. Colocado dentro de un rk-reel, se conecta solo a los valores count y active del padre a través del token de inyección RK_REEL_CONTEXT, sin conectar nada a mano.
<!-- 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 — patrón basado en señales
El output (apiReady) se lanza una vez, después de que el componente se monte y se mida. Emite un objeto ReelApi que puedes guardar y usar para navegar de forma imperativa. Guardar esta referencia en señales de Angular encaja bien con la detección de cambios 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);
}Puntos clave
- Componente standalone
Importa
ReelComponent,RkReelItemDirectivey, si lo necesitas,ReelIndicatorComponentdirectamente en el arrayimportsde tu componente - ng-template + rkReelItem
El equivalente en Angular de la prop
itemBuilderde React: hace posible la virtualización - apiReady
Output que se lanza una vez con la API de navegación imperativa, sin consultas con ViewChild
- afterChange
Emite
{ index, indexInRange }: sigue el índice actual para actualizar la interfaz - OnPush por defecto
Todos los componentes usan
ChangeDetectionStrategy.OnPushy señales de Angular para el máximo rendimiento
Consejos de rendimiento
- Mantén ligeras las plantillas de los slides
La plantilla
rkReelItemse ejecuta para cada slide visible (normalmente 3 a la vez). Evita dentro cálculos pesados o estructuras muy anidadas. - Carga datos cerca del final
Usa
(afterChange)para detectar cuándo el usuario se acerca al final y pide el siguiente lote antes de que se acaben los slides; así puedes tener feeds con scroll infinito. - Usa señales para el estado imperativo
Guarda la referencia a
ReelApiy el índice actual en señales de Angular para tener una reactividad precisa sin re-renderizar todo el componente. - Desactiva la rueda en páginas con scroll
Pon
[enableWheel]="false"cuando el slider esté dentro de un diseño con scroll para no capturar el desplazamiento de la página.
Siguientes pasos
- Referencia de la API de Angular - todos los inputs, outputs y métodos disponibles
- Reel Player - reproductor de vídeo al estilo de TikTok/Reels
- Lightbox - galería de imágenes y vídeos
- Stories Player - visor de stories al estilo de Instagram (próximamente)