#ApplicationRef.tick() instead of NgZone.run() ?

10 messages · Page 1 of 1 (latest)

pallid trellis
#

Hi everyone !
I'm migrating my projects to fully use signals in order to be ready the day when zone.js will become optional. In few part of my projects I use NgZone.run() to detect changes triggered by external libraries callbacks (for example Braintree). As I want to no longer rely on NgZone I wonder if using ApplicationRef.tick() is a proper alternative to trigger change detection. I tried to use ChangeDetectionRef.markForCheck() and even ChangeDetectionRef.detectChanges() but without success in some cases (especially with Braintree dropin, as it use iframes under the hood). So my question is : Isn't too hacky to use ApplicationRef ?

shadow thorn
#

I'd say that you should just, when zoneless is available, remove the call to NgZone.run(): the simple fact of changing the value of a signal read in the view should be sufficient to make Angular detect the change.

pallid trellis
#

Thanks for the answer 🙂 I totally agree and this is what I tend to but in some very specific libs I ended with callbacks setting signals that just don't trigger change detection :

export class BraintreeDropInUiComponent implements OnDestroy {
  private readonly braintree = inject(BraintreeService);
  private readonly applicationRef = inject(ApplicationRef);

  isLoading = signal(true);

  dropInContainer =
    viewChild.required<ElementRef<HTMLElement>>('dropInContainer');

  options = input<DropInOptions | null>(null);
  paymentOptions = input<DropInPaymentOptions | undefined>();

  private dropIn = toSignal(
    // ...
  );

  requestPayment = output<PaymentMethodPayload>();

  private _isPaymentMethodRequestable = signal(false);
  isPaymentMethodRequestable = this._isPaymentMethodRequestable.asReadonly();

  private onPaymentMethodRequestable = () => {
    this._isPaymentMethodRequestable.set(true); // <-- Don't trigger CD
    this.applicationRef.tick();
  };
  private onNoPaymentMethodRequestable = () => {
    this._isPaymentMethodRequestable.set(false); // <-- Don't trigger CD
    this.applicationRef.tick();
  };

  constructor() {
    effect((onCleanup) => {
      this.dropIn()?.on(
        'paymentMethodRequestable',
        this.onPaymentMethodRequestable,
      );
      this.dropIn()?.on(
        'noPaymentMethodRequestable',
        this.onNoPaymentMethodRequestable,
      );

      onCleanup(() => {
        untracked(() => this._isPaymentMethodRequestable.set(false));

        this.dropIn()?.off(
          'paymentMethodRequestable',
          this.onPaymentMethodRequestable,
        );
        this.dropIn()?.off(
          'noPaymentMethodRequestable',
          this.onNoPaymentMethodRequestable,
        );
      });
    });
  }

  //...
}

#

Whoa, I tried to put effect(() => this.isPaymentMethodRequestable()); in my constructor and it work without the need for applicationref.tick() this is insane Oo

shadow thorn
#

I ended with callbacks setting signals that just don't trigger change detection
But zoneless is not supported yet. AFAIK, this should work fine when it is.

vivid siren
#

For now I would do

NgZone.run(() => {
  this._isPaymentMethodRequestable.set(true);
});
pallid trellis
#

Thanks @vivid siren ! yeah I forgot to mention callbacks are in fact outside of the zone (cause of braintree drop in iframes). I'm so glad an angular team member give me a guidance 🙂 I will let my NgZone.run() just like before until v18

shadow thorn
vivid siren