Angular गाइड

@reelkit/angular से स्लाइडर बनाना सीखें।

टच सबसे पहले
momentum और snap के साथ स्वाइप
कीबोर्ड नेविगेशन
तीर वाली कुंजियाँ + Escape
व्हील स्क्रॉल
वैकल्पिक, debounce के साथ
वर्चुअलाइज़्ड
10,000+ आइटम, DOM में 3
इंडिकेटर
Instagram जैसे खिसकते बिंदु
प्रोग्राम से API
apiReady के ज़रिए next(), prev(), goTo()
लूप मोड
अनंत गोल नेविगेशन
दिशा
वर्टिकल या हॉरिज़ॉन्टल
सिग्नल पर आधारित
Angular signals के साथ OnPush

rk-reel कंपोनेंट

rk-reel कंपोनेंट core स्लाइडर कंट्रोलर को लपेटता है। Standalone, ChangeDetectionStrategy.OnPush के साथ।

app.component.ts
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);
  }
}

अपने-आप साइज़

size input वैकल्पिक है। इसे छोड़ने पर rk-reel ResizeObserver से अपना कंटेनर खुद नापता है और CSS से तय लेआउट के हिसाब से ढल जाता है। कंटेनर का साइज़ उसके parent से तय होना चाहिए (जैसे flex, grid या तय CSS आकार)।

html
<!-- 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>

rkReelItem टेम्पलेट पैटर्न

React के render prop की जगह Angular ng-template पर structural directive rkReelItem इस्तेमाल करता है। इससे वर्चुअलाइज़ेशन संभव होता है — सिर्फ़ दिखने वाली स्लाइड बनती हैं। Template context तीन variables देता है:

html
<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>

नेविगेशन के बिल्ट-इन तरीके:

  • टच/स्वाइप: momentum और snap के साथ खींचकर आगे-पीछे जाएँ
  • कीबोर्ड: तीर वाली कुंजियाँ और Escape
  • माउस व्हील: [enableWheel]="true" से चालू करें
  • प्रोग्राम से: next(), prev(), goTo() पाने के लिए (apiReady) output इस्तेमाल करें
typescript
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 स्टेट

createOverlayUrlState किसी overlay के लिए URL-स्टेट कंट्रोलर बनाकर पूरा लौटाता है, फिर आप उसे किसी *UrlOverlay कंपोनेंट को उसके [controller] input के रूप में देते हैं। इसे injection context में कॉल करें — field initialiser में; यह तुरंत जुड़ता है और DestroyRef से छूटता है। खुले होने की स्टेट URL के पास है, इसलिए जुड़ा हुआ overlay खुद खुलता है और आम तौर पर लिंक ही उसे खोलता है। न मौजूद parameter का पहला write history में एक entry जोड़ता है और उसके बाद हर write उसे बदल देता है, इसलिए पेज बदलना back बटन को कभी दबाता नहीं। value/position पढ़ने और प्रोग्राम से चलाने के लिए कंट्रोलर रखें: set(position) खोलता है, set(null) बंद करता है, और set वही निचले स्तर का write है जो overlay स्लाइड बदलने पर अंदर से करता है।

typescript
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),
  });
}

Routing वाला ऐप — Router पर आधारित adapter पास करें, नहीं तो Router की अपनी location पुरानी पड़ जाती है और उसका अगला नेविगेशन parameter गिरा देता है:

typescript
import { createRouterUrlAdapter } from '@reelkit/angular/ng-router-url-adapter';

protected readonly photo = createOverlayUrlState({
  param: 'photo',
  adapter: createRouterUrlAdapter(),
  ...urlIndexKey(() => this.images().length),
});

विकल्पों का object param, codec और locator (तीनों ज़रूरी) लेता है, साथ में वैकल्पिक adapter। codec और locator एक ही Id साझा करने वाली मेल खाती जोड़ी हैं, इसलिए साधारण ?photo=3 गैलरी ...urlIndexKey(() => images().length) spread करती है, जो दोनों हिस्से एक साथ लौटाता है। urlIndexKey index को getter की लौटाई मौजूदा गिनती तक सीमित रखता है, इसलिए पुराना ?photo=99 ठुकराया जाता है और कभी नाम न ली गई स्लाइड खोलने के बजाय URL से अपने-आप हट जाता है। पेज वाली फ़ीड या पहचान से key होने वाली गैलरी इसकी जगह अपनी मेल खाती codec + locator जोड़ी देती है। विकल्पों की पूरी तालिका Angular API रेफ़रेंस में है।

ReelIndicator

एक वैकल्पिक कंपोनेंट, जो Instagram जैसे प्रोग्रेस इंडिकेटर दिखाता है कि आप स्लाइडर में अभी किस जगह पर हैं। rk-reel के अंदर रखने पर यह RK_REEL_CONTEXT injection token के ज़रिए parent की count और active values से अपने-आप जुड़ जाता है — हाथ से जोड़ने की ज़रूरत नहीं।

html
<!-- 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" />

apiReady output — सिग्नल पर आधारित पैटर्न

(apiReady) output कंपोनेंट के mount होने और नापे जाने के बाद एक बार चलता है। यह एक ReelApi object देता है, जिसे आप रख सकते हैं और imperative नेविगेशन के लिए इस्तेमाल कर सकते हैं। इस reference के लिए Angular signals इस्तेमाल करना OnPush change detection के साथ साफ़ तरीके से काम करता है।

typescript
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);
}

मुख्य बातें

  • Standalone कंपोनेंट

    ReelComponent, RkReelItemDirective और ज़रूरत हो तो ReelIndicatorComponent को सीधे अपने कंपोनेंट के imports array में import करें

  • ng-template + rkReelItem

    React के itemBuilder prop का Angular वाला रूप — वर्चुअलाइज़ेशन संभव बनाता है

  • apiReady

    output, जो imperative नेविगेशन API के साथ एक बार चलता है — ViewChild query की ज़रूरत नहीं

  • afterChange

    { index, indexInRange } emit करता है — UI अपडेट के लिए मौजूदा index पर नज़र रखें

  • डिफ़ॉल्ट रूप से OnPush

    सबसे अच्छे परफ़ॉर्मेंस के लिए सारे कंपोनेंट ChangeDetectionStrategy.OnPush और Angular signals इस्तेमाल करते हैं

परफ़ॉर्मेंस टिप्स

  • स्लाइड templates को हल्का रखें

    rkReelItem template हर दिखने वाली स्लाइड के लिए चलता है (आम तौर पर एक बार में 3)। इसके अंदर भारी गणना या बहुत गहरी nested संरचना से बचें।

  • किनारे के पास डेटा लोड करें

    (afterChange) से पहचानें कि यूज़र आख़िर के पास पहुँच रहा है, और स्लाइड ख़त्म होने से पहले अगला बैच fetch करें — इससे अनंत स्क्रॉल वाली फ़ीड बनती है।

  • imperative स्टेट के लिए signals इस्तेमाल करें

    पूरे कंपोनेंट को दोबारा रेंडर किए बिना बारीक reactivity पाने के लिए ReelApi reference और मौजूदा index को Angular signals में रखें।

  • स्क्रॉल होने वाले पेज में व्हील बंद करें

    जब स्लाइडर स्क्रॉल होने वाले लेआउट के अंदर हो, तो पेज का स्क्रॉल पकड़ने से बचने के लिए [enableWheel]="false" सेट करें।

अगले कदम