Reputation: 959
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
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
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