2

I've added Angularfire to my Angular project and am using the authentication. Everything is work however, my Resolve Navigation Guard is stopping activation of the component when the resolve is an error.

Ref: https://blog.thoughtram.io/angular/2016/10/10/resolving-route-data-in-angular-2.html

QUESTION:

How can I make my dashboard route not activate unless auth has been resolved AND the resolve does not catch an error?

This @Injectable is the AuthGuard

import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { myFirebaseAuthConfig } from "./auth";
import { AngularFire } from "angularfire2";

@Injectable()
export class CanActivateViaAuthGuard implements Resolve<any> {
  constructor(private af: AngularFire) {}

  resolve() {
    return this.af.auth.login(myFirebaseAuthConfig)
      .then(data => {
        console.log(data, 'data');
        return data;
      })
      .catch(err => {
        console.log(err, 'err');
        return err;
      });
  }
}

The Module:

@NgModule({
  imports: [
    routing,
    AngularFireClientModule,
  ],
  providers: [CanActivateViaAuthGuard],
  declarations: [AdminComponent, LoginComponent, SignupComponent],
  exports: [AdminComponent],
})
export class AdminRouteModule {}

The Route

import { Routes, RouterModule } from "@angular/router";
import { AdminComponent } from "./admin.component";
import { ModuleWithProviders } from "@angular/core";
import { LoginComponent } from "../login/login.component";
import { SignupComponent } from "../signup/signup.component";
import { CanActivateViaAuthGuard } from "../firebase/auth.service";

export const routerConfig: Routes = [
  { path: '', redirectTo: 'login', pathMatch: 'full' },
  {
    path: 'dashboard',
    component: AdminComponent,
    resolve: [CanActivateViaAuthGuard],
  },
  {path: 'login', component: LoginComponent},
  {path: 'signup', component: SignupComponent},
];

export const routing: ModuleWithProviders = RouterModule.forChild(routerConfig);
Armeen Moon
  • 18,061
  • 35
  • 120
  • 233
  • I'd try to create an `canActivate` guard and inject your `CanActivateViaAuthGuard` (which should be named `...Resolver`) and then get the auth data from `CanActivateViaAuthGuard` and if it returns the expected value, return `true`, otherwise `false`. See also http://stackoverflow.com/questions/36271899/what-is-the-correct-way-to-share-the-result-of-an-angular-2-http-network-call-in/36291681#36291681 for how to avoid multiple requests to the server. – Günter Zöchbauer Apr 23 '17 at 09:52
  • Hey @GünterZöchbauer I'm still really struggling to complete this. it seems as if Resolver and canActivate fire at the same will the DI remedy this problem? – Armeen Moon Apr 27 '17 at 18:55
  • I don't think they fire. First `canActivate` needs to resolve to `true`, then the resolver will be executed, then the component will be created. If you need to communicate between `canActivate` guard and resolver, the cleanest solution would be a shared service. You can also try to inject the guard into the resolver. – Günter Zöchbauer Apr 27 '17 at 20:17

1 Answers1

0

Guards and Resolvers are two different interfaces and have two different tasks:

Guards: Check a certain condition, only if this condition is met, go to the specified route.

Resolver: Resolve data needed for the specific component.

So if you want to keep a Component from being loaded in a certain condition, you would do this via a Guard:

@Injectable()
export class GuardService implements CanActivate, CanActivateChild {

  // in your case it would be AngularFire and not MyService
  constructor(private myService: MyService,
    private router: Router) {
  }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    return this.checkAuthentication();
  }

  canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    return this.checkAuthentication();
  }

  private checkAuthentication(): boolean {
    return this.myService.canIGoToThisRoute();
  }
}

As you can see, the Guard implements the canActivate interface. In your RoutingModule you would then have:

export const routerConfig: Routes = [
  { path: '', redirectTo: 'login', pathMatch: 'full' },
  {
    path: 'dashboard',
    component: AdminComponent,
    // -> changed to canActivate, don't forget to add to providers
    canActivate: [GuardService], 

  },
  {path: 'login', component: LoginComponent},
  {path: 'signup', component: SignupComponent},
];

export const routing: ModuleWithProviders = RouterModule.forChild(routerConfig);

If you still want to do it in the resolver for some reason, then inject the router and redirect on error.

seBaka28
  • 932
  • 1
  • 7
  • 16