angulardependency-injectionangular-routingangular-resolver

Angular resolver inject router


I defined a resolver in my Angular (v13) app to do some shenanigans with a wordpress backend. When the user accesses an URL I want the resolver to check post type and ID from wordpress and route accordingly (post list, single post, page, ...).

To do that I have injected Angular's Router in my WordpressResolver class, but when I call router.navigate(...) I get the following error :

ERROR TypeError: Cannot read properties of undefined (reading 'navigate')

It seems like the router is not injected, but I cannot figure out why. I added the @Injectable decorator on my resolver and provided it in the routing module.
Here are my routing module and resolver code.

app.module.routing.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { NotFoundComponent } from './modules/not-found/not-found.component';
import { WordpressResolver } from './wordpress-resolver';

const routes: Routes = [
  {
    path: 'posts',
    loadChildren: () =>
      import('./modules/posts/posts.module').then((m) => m.PostsModule),
  },
  {
    path: 'single/:id',
    loadChildren: () =>
      import('./modules/post/post.module').then((m) => m.PostModule),
  },
  {
    path: 'page/:id',
    loadChildren: () =>
      import('./modules/page/page.module').then((m) => m.PageModule),
  },
  {
    path: '**',
    component: NotFoundComponent,
    resolve: {
      error: WordpressResolver,
    },
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: [WordpressResolver],
})
export class AppRoutingModule {}

wordpress-resolver.ts

import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import {
  ActivatedRouteSnapshot,
  Resolve,
  Router,
  RouterStateSnapshot,
} from '@angular/router';
import { environment } from 'src/environments/environment';
import { UrlData } from './shared/model/url-data.interface';
import { UrlType } from './shared/model/url-type.enum';

@Injectable({ providedIn: 'root' })
export class WordpressResolver implements Resolve<HttpErrorResponse | null> {
  constructor(private http: HttpClient, private router: Router) {}

  private error: HttpErrorResponse | null = null;

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): HttpErrorResponse | null {
    this.http
      .get<UrlData>(environment.domain + state.url)
      .subscribe(this.resolveSuccess, this.resolveError);

    return this.error;
  }

  private resolveSuccess(urlData: UrlData): void {
    switch (urlData.type) {
      case UrlType.POSTS:
        this.router.navigate(['/posts'], { skipLocationChange: false });
        return;
      case UrlType.POST:
        this.router.navigate(['/post', urlData.id], {
          skipLocationChange: false,
        });
        return;
      case UrlType.PAGE:
        this.router.navigate(['/page', urlData.id], {
          skipLocationChange: false,
        });
        return;
      default:
        return;
    }
  }
  private resolveError(error: HttpErrorResponse): void {
    this.error = error;

    this.router.navigate(['/error'], {
      skipLocationChange: true,
    });
  }
}

(I know '/error' is not mapped yet, this is a work in progress)


Solution

  • You have to define your callback function as arrow function so that you can access global scope

    resolveSuccess= (urlData: UrlData): void => { }