I'm trying to use the routerCanDeactivate function for a component in my app. The simple way to use it is as follows:
routerCanDeactivate() {
return confirm('Are you sure you want to leave this screen?');
}
My only issue with this is that it's ugly. It just uses a browser generated confirm prompt. I really want to use a custom modal, like a Bootstrap modal. I have the Bootstrap modal returning a true or false value based on the button they click. The routerCanDeactivate I'm implementing can accept a true/false value or a promise that resolves to true/false.
Here is the code for the component that has the routerCanDeactivate method:
export class MyComponent implements CanDeactivate {
private promise: Promise<boolean>;
routerCanDeactivate() {
$('#modal').modal('show');
return this.promise;
}
handleRespone(res: boolean) {
if(res) {
this.promise.resolve(res);
} else {
this.promise.reject(res);
}
}
}
When my TypeScript files compile, I get the following errors in the terminal:
error TS2339: Property 'resolve' does not exist on type 'Promise<boolean>'.
error TS2339: Property 'reject' does not exist on type 'Promise<boolean>'.
When I try to leave the component, the modal starts, but then the component deactivates and doesn't wait for the promise to resolve.
My issue is trying to work out the Promise so that the routerCanDeactivate method waits for the promise to resolve. Is there a reason why there is an error saying that there is no 'resolve' property on Promise<boolean>? If I can work that part out, what must I return in the routerCanDeactivate method so that it waits for the resolution/rejection of the promise?
For reference, here is the DefinitelyTyped Promise definition. There is clearly a resolve and reject function on there.
Thanks for your help.
UPDATE
Here is the updated file, with the Promise being initialized:
private promise: Promise<boolean> = new Promise(
( resolve: (res: boolean)=> void, reject: (res: boolean)=> void) => {
const res: boolean = false;
resolve(res);
}
);
and the handleResponse function:
handleResponse(res: boolean) {
console.log('res: ', res);
this.promise.then(res => {
console.log('res: ', res);
});
}
It still doesn't work correctly, but the modal shows up and waits for the response. When you say yes leave, it stays on the component. Also, the first res that is logged is the correct value returned from the component, but the one inside .then function is not the same as the one passed in to the handleResponse function.
More Updates
After doing some more reading, it appears that in the promise declaration, it sets the resolve value, and the promise has that value no matter what. So even though later on I call the .then method, it doesn't change the value of the promise and I can't make it true and switch components. Is there a way to make the promise not have a default value and that it has to wait until the its .then method is invoked?
Updated functions:
private promise: Promise<boolean> = new Promise((resolve, reject) => resolve(false) );
handleResponse(res: any) {
this.promise.then(val => {
val = res;
});
}
Thanks again for the help.
Last Update
After looking at many suggestions, I decided to create a Deferred class. It's worked pretty well, but when I do the deferred.reject(anyType), I get an error in the console of:
EXCEPTION: Error: Uncaught (in promise): null
This same thing happens when I pass in null, a string, or a boolean. Trying to provide a catch function in the Deferred class didn't work.
Deferred Class
export class Deferred<T> {
promise: Promise<T>;
resolve: (value?: T | PromiseLike<T>) => void;
reject: (reason?: any) => void;
constructor() {
this.promise = new Promise<T>((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
this.promise? More accurately, shouldn't you return a promise, and keep reference of itsresolve&rejectfunctions, then call these?private promise = new Promise<boolean>((resolve, reject) => resolve(false));