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; }); } }
The promise in TypeScript is used to make asynchronous programming. The promise can be used when we want to handle multiple tasks at the same time. By the use of TypeScript promise, we can skip the current operation and move to the next line of the code.
To create a promise in Angular we just need to use 'new Promise(function)' syntax. The promise constructor takes function as a parameter and that inner function takes resolve and reject as a params.
To create a promise, we use new Promise(executor) syntax and provide an executor function as an argument. This executor function provides a means to control the behavior of our promise resolution or rejection. In TypeScript, we can provide the data type of the value returned when promise fulfills .
In TypeScript, promise type takes an inner function, which further accepts resolve and rejects as parameters. Promise accepts a callback function as parameters, and in turn, the callback function accepts two other parameters, resolve and reject. If the condition is true, then resolve is returned; else, returns reject.
I'm not familiar with the bootstrap modal api, but I'd expect there to be a way to bind to a close event somehow when creating it.
export class MyComponent implements CanDeactivate { routerCanDeactivate(): Promise<boolean> { let $modal = $('#modal').modal(); return new Promise<boolean>((resolve, reject) => { $modal.on("hidden.bs.modal", result => { resolve(result.ok); }); $modal.modal("show"); }); } }
You're trying to use the Promise
like Deferred
. If you want that kind of API, write yourself a Deferred
class.
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; }); } } export class MyComponent implements CanDeactivate { private deferred = new Deferred<boolean>(); routerCanDeactivate(): Promise<boolean> { $("#modal").modal("show"); return this.deferred.promise; } handleRespone(res: boolean): void { if (res) { this.deferred.resolve(res); } else { this.deferred.reject(res); } } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With