angular routing giving ObjectUnsubscribedError in zone.js

118 views Asked by At

We are attempting to upgrade an Angular application from V8 to V15. Our router uses lazy loading in case that is relevant. When attempting to move between components no navigation takes place and we are getting the error:

    core.mjs:8400 ERROR Error: Uncaught (in promise): ObjectUnsubscribedError: object unsubscribed
    at resolvePromise (zone.js:1211:31)
    at resolvePromise (zone.js:1165:17)
    at zone.js:1278:17
    at _ZoneDelegate.invokeTask (zone.js:406:31)
    at core.mjs:23896:55
    at AsyncStackTaggingZoneSpec.onInvokeTask (core.mjs:23896:36)
    at _ZoneDelegate.invokeTask (zone.js:405:60)
    at Object.onInvokeTask (core.mjs:24197:33)
    at _ZoneDelegate.invokeTask (zone.js:405:60)
    at Zone.runTask (zone.js:178:47)

We have attempted to systematically ensure all subscriptions are unsubscribed from in our ngOnDestroy lifecycle hook, so far without luck. When we put in breakpoints in zone.js there does not seem to be any indication of what promise in particular. Is there any way to dig into the error object to trace where exactly we need to change how we are unsubscribing.

Currently this is our method of closing subscriptions (with various code skipped of course):

  private unsubscribe: Subject<void> = new Subject();

    this.router.events
      .pipe(takeUntil(this.unsubscribe))
      .subscribe(e => {
      if (e instanceof  NavigationEnd) {
        this.isOnHomePage = e.url === AppUrls.home();
      }
    });
    
ngOnDestroy() {
    this.unsubscribe.next();
    this.unsubscribe.complete();
  }

Another possible clue to our woes: often if a route navigation fails with the ObjectUnsubscibedError above, a second click will cause the route to load without the error...

At this point I am confused as to which module could even be the source of the core problem. Any debugging suggestions?

1

There are 1 answers

0
JSmart523 On

Do your best to avoid subscribing.

Instead, try the following

private _destroy$: Subject<void> = new Subject();

    this.isOnHomePage$ = concat(
      of(initialization value goes here),
      this.router.events.pipe(
        takeUntil(this._destroy$),
        filter(e => e instanceof  NavigationEnd),
        map(e => (e.url === AppUrls.home())),
        shareReplay(1) // make it similar to a BehaviorSubject
      )
    );
    
  ngOnDestroy() {
    const d$ = this._destroy$;
    if d$ {
      d$.next();
      d$.complete();
    }
  }

and then, instead of subscribing, stay in the world of observables and use the async pipe in your template, letting Angular do the subscribing work for you. At that point, You don't need to unsubscribe, that's just for safety.