Angular 6 UI Router class resolvers

Issue

I’m currently working on a AngularJS to Angular 6 migration, so I’m using the Angular UI Router for the transitions (@uirouter/angularjs, @uirouter/angular, @uirouter/angular-hybrid).

I’ve seen that you can use resolver classes to resolve data in your routes, but as far as I can see this is only available in @angular/router. I’ve read through the documentation and the source code and did see references in the UI router to the “normal” router, so I’m assuming that the UI router is simply extending the “normal” router.

Now my question is: is there a way I can use the resolver classes in UI router without having to make use of the provided Resolvables?

The desired usage as displayed in @angular/router:

const routes = {
  states: [{
    name: 'page',
    url: '/page/:slug/',
    component: ViewPageComponent,
    resolve: {
      boatData: PageDataResolver
    },
    parent: 'public'
  }]
};

//Module for the states, the forRoot is defined in my app.module:
@NgModule({
  imports: [
    UIRouterUpgradeModule.forChild(routes)
  ],
  providers: [
    PageDataResolver,
    PageService //di in the PageDataResolver
  ]
})
export class PublicRoutesModule {}

And the contents of the PageDataResolver:

import {Injectable} from '@angular/core';
import {NgRedux} from '@angular-redux/store';
import {PageService} from '../page.service';
import {IAppState} from '../../../redux/interfaces/state';
import {Resolve} from "@angular/router";

@Injectable()
export class BoatDataResolver implements Resolve<Promise<any>> {

    constructor(
        private _page: PageService,
        private ngRedux: NgRedux<IAppState>
    ) {}

    resolve(route): Promise<any> {
        return this._page.getPageInformation(
            this.ngRedux.getState().currentLanguage,
            route.paramMap.get('slug')
        ).toPromise();
    }
}

However, when I use this exact snippet, I get a runtime error saying “No provider for _page!”. So it seems that the services I inject, aren’t actually injected in the StateDeclaration.

I hope you can help, thanks for your time!

Solution

FYI UI-Router for Angular (@uirouter/angular) is a totally separate implementation — it does not extend the Angular Router (@angular/router).

The resolve system in @uirouter/angular works much the same as the resolve system of the AngularJS version. The main difference is that services in AngularJS must always be injected using a string token. In Angular, services may be injected using Token objects (OpaqueToken or InjectionToken) or class references.

In hybrid mode (@uirouter/angular-hybrid), UI-Router resolves are always injected using string tokens. If you want to use non-string tokens in a resolver, inject the Transition object using the $transition$ string token. Then, use the Transition.injector() API to access your services.

export const pageDataResolver = ($transition$) => {
  const _page: PageService = $transition$.injector().get(PageService);
  const ngRedux: NgRedux<IAppState> = $transition$.injector().get(NgRedux);

  return this._page.getPageInformation(
            this.ngRedux.getState().currentLanguage,
            route.paramMap.get('slug')
        ).toPromise();
}

const routes = {
  states: [{
    name: 'page',
    url: '/page/:slug/',
    component: ViewPageComponent,
    resolve: {
      boatData: PageDataResolver
    },
    parent: 'public'
  }]
};

I’ve added docs to @uirouter/angular-hybrid which explains this limitation:
https://github.com/ui-router/angular-hybrid#resolve

Answered By – Chris T

Answer Checked By – Clifford M. (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.