0

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?

lampshade
  • 2,470
  • 3
  • 36
  • 74

1 Answers1

0

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

OZ_
  • 12,492
  • 7
  • 50
  • 68
  • Thanks for you answer. I know that it works without the CD call. But as you said it throws the famous Angular error and I don't want that. :) – lampshade Aug 22 '23 at 08:11
  • Well, it turned out to be a really interesting question. Maybe it's somehow related to the words of Alex Rickabaugh that structural directives are not quite aligned with the signals. I've created another variation of your code, without custom schemas and ngAfterViewInit() call: https://stackblitz.com/edit/stackblitz-starters-nzedb6?file=src%2Fmain.ts – OZ_ Aug 22 '23 at 09:06
  • 1
    @OZ_ When you update a signal, the lView consummer only calls `markViewDirty()` (that's the same function called by `markForCheck`). It won't trigger any CD, `appRef.tick()` is required for that ! – Matthieu Riegler Aug 22 '23 at 11:36