swordfish
swordfish

Reputation: 959

Ionic 4 - How to implement canActivate on one route using guards

I have two routes in my ionic app, welcome page and home page, inside welcome the user must answer a question, after that, he gets redirect to home page, what i am trying to do, is to skip the first route if the user already answered the question.

Here is my guard:

export class CheckWelcomeGuard implements CanActivate {
constructor(private router: Router, private dbstorage: DatabaseService) {}

canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
): boolean {
    this.dbstorage.getDatabaseState().subscribe(ready => {
    if (ready) {
        this.dbstorage.checkSettingsValue("WELCOME_PAGE_PASSED").then(data => {
        if (data) {
            return true;
        }
        });
    }
    });
    this.router.navigate(["/welcome"], {
    queryParams: { redirect: state.url },
    replaceUrl: true
    });
    return false;
}
}

Depends on that, the redirect happens before the check get even executed! continuously keeping redirect to welcome page!

private database: SQLiteObject;
private dbReady: BehaviorSubject<boolean> = new BehaviorSubject(false);

settings = new BehaviorSubject([]);

constructor(
private platform: Platform,
private sqlite: SQLite,
private http: HttpClient
) {
this.platform.ready().then(() => {
    this.createDatabaseObject();
});
}

createDatabaseObject() {
this.sqlite
    .create({
    name: "testapp.db",
    location: "default"
    })
    .then((db: SQLiteObject) => {
    this.database = db;
    this.seedDatabase();
    });
}

seedDatabase() {
this.http
    .get("assets/testapp.sql", { responseType: "text" })
    .subscribe(sql => {
    this.database.executeSql(sql, [])
        .then(() => {
        // this.clearSettings();
        console.log('Executed SQL');
        this.dbReady.next(true);
        })
        .catch(e => console.log(e));
    });
}

// Database state
// ready: true
// not ready: false
getDatabaseState() {
    return this.dbReady.asObservable();
}

Upvotes: 0

Views: 1185

Answers (2)

Reqven
Reqven

Reputation: 1778

I'm not sure this is the way to go, but here is a way to do it.

I'm assuming both getDatabaseState() and checkSettingsValue() return an Observable.
Angular will automatically subscribe to the Observable returned by the canActivate method.

export class CheckWelcomeGuard implements CanActivate {

  constructor(
    private router: Router,
    private dbstorage: DatabaseService
  ) {}

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

    return this.dbstorage.getDatabaseState()
      .pipe(
        switchMap(ready => !ready
          ? this.redirectToWelcome(state)
          : this.dbstorage.checkSettingsValue('WELCOME_PAGE_PASSED')
            .pipe(
              switchMap(data => !data
                ? this.redirectToWelcome(state)
                : of(true)
              )
            )
        )
      );
  }

  redirectToWelcome(state: RouterStateSnapshot): Promise<boolean> {
    return this.router.navigate(['/welcome'], {
      queryParams: { redirect: state.url },
      replaceUrl: true
    });
  }

}

Upvotes: 1

alexortizl
alexortizl

Reputation: 2680

Problem is that your call to this.dbstorage.getDatabaseState().subscribe... is an async operation so it is non-blocking. That means that the Browser will make the call but won't wait for an answer instead it will continue normal execution of your code which in this case is this.router.navigate(["/welcome"], .... Because of that this.router.navigate(["/welcome"],... will be always called regardless the result of this.dbstorage.getDatabaseState().subscribe.... You need to have your code wait for the database response. I like doing it with Promises like this:

export class CheckWelcomeGuard implements CanActivate {
    constructor(private router: Router, private dbstorage: DatabaseService) { }

    async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        const ready = await this.dbstorage.getDatabaseState().toPromise();

        if (ready) {
            const data = await this.dbstorage.checkSettingsValue("WELCOME_PAGE_PASSED");

            if (data) {
                return true;
            } else {
                this.router.navigate(["/welcome"], {
                    queryParams: { redirect: state.url },
                    replaceUrl: true
                });
                return false;
            }
        } else {
            this.router.navigate(["/welcome"], {
                queryParams: { redirect: state.url },
                replaceUrl: true
            });
            return false;
        }

    }
}

canActivate also accepts Observables as a return value and works as well. It's another way of doing it.

Upvotes: 1

Related Questions