Search code examples
angulartypescriptsignalsswiper.jsangular-signals

Is this the right way to use Angular's signals? And why do I still need ChangeDetectorRef.detectChanges()?


I'm trying to use signals and I'm wondering if the use case here is actually benefical from not using Signals and simply have a "normal" class member swiper?: Swiper:

@Component({
  selector: '[app-test]',
  standalone: true,
  imports: [CommonModule],
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TestComponent implements AfterViewInit {
  swiper = signal<Swiper|undefined>(undefined);

  constructor(
    private readonly cd: ChangeDetectorRef,
    private readonly elementRef: ElementRef,
  ) { }

  ngAfterViewInit(): void {
    const swiper: unknown = this.elementRef.nativeElement.querySelector('swiper-container')?.swiper;

    if (swiper instanceof Swiper) {
      this.swiper.set(swiper);
      this.cd.detectChanges();
    }
  }

  slideTo(direction: 'prev'|'next'): void {
    const swiper = this.swiper();
    if (!swiper) return;
    direction === 'prev' ? swiper.slidePrev() : swiper.slideNext();
  }
}

And in the template:

<fieldset *ngIf="swiper() as swiper">
  <button
    type="button"
    [attr.disabled]="swiper.activeIndex === 0 ? true : null"
    (click)="slideTo('prev')"
  >Prev</button>

  <button
    class="slider-navigation slider-navigation--next"
    type="button"
    [attr.disabled]="swiper.activeIndex === swiper.slides.length - 1 ? true : null"
    (click)="slideTo('next')"
  >Next</button>
</fieldset>

I do understand that I need to use this.cd.detectChanges(); since ngAfterViewInit runs after Angular's change detection. But shouldn't Signales take care of it? It makes the code look exactly like not using Signals at all. Where's the benefit? Am I using it correctly here?


Solution

  • After some collective investigation, we found that the issue (in both versions of the code) is caused by the fact that the Change Detection cycle finishes after you set a signal.

    That's why markForCheck() doesn't work (signal already marks the views), and detectChanges() works - another CD cycle is needed to check all the marked views.

    Confirmed by Alex Rickabaugh.


    Previous answer:

    Here is your code on StackBlitz: https://stackblitz.com/edit/stackblitz-starters-1tnwze?file=src%2Fmain.ts

    It works without a cd.detectChanges() call. It just prints one famous error to the console in dev mode ;)