1

I have an authentication service which is used in both the admin and user portions of my site, but connects to different endpoints on the backend API. I am using Factories to provide the different services based on which module is loaded (admin vs user).

However, when I attempt to create an AuthGuard service and inject my AuthService, it always generates a separate instance of the AuthService when the router calls CanActivate. Is the router somehow working outside the scope of the lazy-loaded module and that is why it's generating a new instance?

auth.provider.ts

let AuthFactory = (
    http: Http,
    router: Router,
    storageService: StorageService,
    modalService: NgbModal,
    isAdmin: boolean
  ) => {
  return new AuthService(http, router, storageService, modalService, isAdmin);
};

export let AuthProvider = {
  provide: AuthService,
  useFactory: AuthFactory,
  deps: [ Http, Router, StorageService, NgbModal, IS_ADMIN ]
};

app.routing.ts

const appRoutes: Routes = [
  {
    path: 'mgmt',
    loadChildren: 'app/admin/admin.module'
  },
  { path: '',
    loadChildren: 'app/users/users.module'
  },
  { path: '**',
    redirectTo: ''
  }
];
@NgModule({
  imports: [ RouterModule.forRoot(appRoutes)],
  exports: [ RouterModule ]
})

export class AppRoutingModule {}

admin.routing.ts

RouterModule.forRoot(appRoutes);

const routes: Routes = [
  { path: '',
    component: AdminComponent,
    children: [
      {
        path: '',
        component: DashboardComponent,
        canActivate: [AuthGuard],
      {
        path: '**',
        redirectTo: ''
      }
    ]
  }
];

@NgModule({
  imports: [ RouterModule.forChild(routes)],
  exports: [ RouterModule ]
})

export class AdminRoutingModule {}

auth-guard.service.ts

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService,
              private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return this.authService.authenticated()
      .map(authenticated => {
        if (authenticated) {
          return true;
        } else {
          // do some other things
          return false;
        }
      })
      .take(1);
  }
}
Matthew Wise
  • 55
  • 1
  • 5

1 Answers1

2

You are constructing a new AuthProvider each time because of a new use of AuthFactory. You should move to a "singleton" design pattern, where the factory provides the constructor and then a getter method returns either the existing AuthProvider or a new one, if it does not already exist. That way you're always referencing a single instance of the object.

Check out plenty of resources on implementing singletons in javascript.

Corbfon
  • 3,514
  • 1
  • 13
  • 24
  • That's exactly the advice I needed, thanks for putting me in the right direction. – Matthew Wise Dec 11 '16 at 11:19
  • Actually, I have to retract that, after refactoring to use a simple provide:AuthService, useClass: AdminAuthService which should be providing a singleton, I'm still getting two services created. I could use the singleton design pattern you describe(http://stackoverflow.com/questions/36198785/how-do-i-create-a-singleton-service-in-angular-2), but then I am bypassing the built in mechanism for dependency injection in Angular2. What I need is to understand why Angular's dependency injection is failing in this case. – Matthew Wise Dec 11 '16 at 12:02
  • Perhaps letting Angular 2 govern the injection of your auth.provider.ts would do the trick? Try making that a class and exporting it with @injectable() decorator, same as you have in auth-guard.service – Corbfon Dec 12 '16 at 03:26
  • Also, the question that you referenced above makes me ask where you've added these components to the 'providers'? – Corbfon Dec 12 '16 at 03:43
  • I'm using NgModule and adding it to the providers entry in the lazy loaded component. I've gone ahead and implemented the singleton for now which solves my immediate problem. Really do appreciate the feedback, after several hours of frustration it's good to have a solution. – Matthew Wise Dec 13 '16 at 10:12
  • @MatthewWise if my answer solved the issue, would you mark it as the answer please? – Corbfon Jan 13 '17 at 16:46