1

i followed a lot of tutorials and tried many combinations but cannot make this work.

I need to let a route be available when a user is logged. If he's not i need to redirect him to the homepage (this._popupService.setCallbackRoute(route.url.join('/'))) and show a popup (this._popupService.showPopUp()) which lets him login or register.

I cannot get the syncronized value from the authService. This is my code:

app.module.ts

imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule.forRoot(
        {path: '', component: HomepageComponent},
        {
            path: 'protectedRoute',
            component: SubmitComponent,
            data: {requiresLogin: true},
            canActivate: [AccessGuard]
        }
    ),
    ...
]

auth.service.ts

 @Injectable()
export class AuthService {

    private loggedIn: Subject<boolean> = new Subject<boolean>();

    get isLoggedIn() {
        return this.loggedIn.asObservable();
    }

    login(user: IUser) {
        return this._http.get('assets/api/responseSuccess.json?email=' + user.email + '&password=' + user.password)
            .map((responseLogin => {
                const jsonResponse = responseLogin.json();
                if (jsonResponse.response === 'success') {
                    const userResponse: IUser = jsonResponse.user;
                    this._sessionService.setUserSession(userResponse);
                    this.loggedIn.next(true);
                    return true;
                } else {
                    this._customFlashMessages.show('Got error login, please check your credentials and try again!');
                    return false;
                }
            }));
    }
}

accessGuard.service.ts

import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router} from '@angular/router';
import {Observable} from 'rxjs/Observable';
import {AuthService} from './auth.service';
import {PopUpService} from './popup.service';

@Injectable()
export class AccessGuard implements CanActivate {
    loggedIn$: Observable<boolean>;

    constructor(private authService: AuthService, private _popupService: PopUpService, private router: Router) {
        this.loggedIn$ = this.authService.isLoggedIn;
    }

    canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | Promise<boolean> | boolean {
        const requiresLogin = route.data.requiresLogin || false;
        if (requiresLogin) {
            this.authService.isLoggedIn.subscribe( // even tried with .map()
                result => {
                    console.log(result); // Logs a promise object
                    if (!result) {
                        console.log("protected route"); // Never reached
                        // If not logged in shop popup and stay on that page
                        this._popupService.showPopUp();
                        this._popupService.setCallbackRoute(route.url.join('/'));
                        return false;
                    }
                    console.log('logged in'); // Never reached
                    return true;
                });
        }
        return true;
    }
}

I tried several things. My code works if i directly check the sessionStorage('user') but not working with observable.

Any help is appreciated.

Thank you!

SBO
  • 623
  • 2
  • 8
  • 22
  • From your explanation, popup has to be not in guard, but in homepage component, inside which one can implement `ngOnInit()` a check if user `isSignedIn` and if not, fire the popup with the login form. Guard has to only redirect to the homepage before returning `false`. – Vadim Aug 25 '17 at 19:47

2 Answers2

1

This is the correct answer, the problem was in the logic inside the subscribe method.

Here's the correct accesGuard.service.ts

@Injectable()
export class AccessGuard implements CanActivate {

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

    canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | Promise<boolean> | boolean {
        const requiresLogin = route.data.requiresLogin || false;
        if (requiresLogin) {
            this.authService.isLoggedIn.subscribe(
                result => {
                    if (!result) {
                        // If not logged in shop popup and stay on that page
                        this._popupService.showPopUp();
                        this._popupService.setCallbackRoute(route.url.join('/'));
                        this.router.navigate(['/']);
                        return false;
                    }
                    return true;
                });

            return true;
        }else{

        return true;
       }
    }
}
SBO
  • 623
  • 2
  • 8
  • 22
  • @Robin Dijkhof logic is allowed in canActivate method. In this way you can use the same procedure for every authenticated routes. – SBO Aug 25 '17 at 22:51
  • Logic is allowed, but displaying a model isn't just logic. Also are you sure this is working. Subscribe is asynchronous so this looks weird. I suspect it will never be false. If it works you are lucky, but what happens if there is a small delay. – Robin Dijkhof Aug 26 '17 at 08:47
  • Uhm.. it works even in production server. Do you mean that i should return the this.authService.isLoggedIn.subscribe() result instead of returning true at the end? – SBO Aug 26 '17 at 10:15
  • take a look at this question to see what I mean: https://stackoverflow.com/questions/45847183/subscribe-function-doesnt-work-correctly – Robin Dijkhof Aug 28 '17 at 13:34
  • Yes i understood. I updated my code with the else statement. This should work in every case. – SBO Aug 28 '17 at 14:14
  • That is not exactly what I meant. Try this for example: `this.authService.isLoggedIn.delay(1000)`. And add the followinf import: `import 'rxjs/add/operator/delay';` – Robin Dijkhof Aug 28 '17 at 14:17
0

You need to return an observable.

canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    const requiresLogin = route.data.requiresLogin || false;
    if (requiresLogin) {
        return this.authService.isLoggedIn;
    }
    return Observable.of(false);
}
Robin Dijkhof
  • 18,665
  • 11
  • 65
  • 116
  • And how can i show the popup and put the login inside every private page? – SBO Aug 25 '17 at 19:21
  • I don't think that is possible from a guard. I think it would be better to redirect to a dedicated loginpage and send the requested with it. After a succesfull login you can redirect back. – Robin Dijkhof Aug 25 '17 at 20:13
  • I would like to do something like this: https://stackoverflow.com/questions/39935721/how-to-return-observable-from-subscribe – SBO Aug 25 '17 at 20:31
  • See my comment above. Thank you for your answer. – SBO Aug 25 '17 at 22:51