我正在尝试将routerCanDeactivate
功能用于我的应用中的组件。使用它的简单方法如下:
routerCanDeactivate() {
return confirm('Are you sure you want to leave this screen?');
}
我唯一的问题是它很难看。它只是使用浏览器生成的确认提示。我真的想使用自定义模态,如Bootstrap模式。我有Bootstrap模式根据他们单击的按钮返回true或false值。我正在实现的routerCanDeactivate
可以接受真/假值或解析为真/假的承诺。
以下是具有routerCanDeactivate
方法的组件的代码:
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);
}
}
}
当我的TypeScript文件编译时,我在终端中收到以下错误:
error TS2339: Property 'resolve' does not exist on type 'Promise<boolean>'.
error TS2339: Property 'reject' does not exist on type 'Promise<boolean>'.
当我尝试离开组件时,模态启动,但随后组件停用并且不等待承诺解析。
我的问题是尝试计算Promise,以便routerCanDeactivate
方法等待解决的承诺。是否有一个错误说明'resolve'
上没有Promise<boolean>
属性?如果我可以解决这个问题,我必须在routerCanDeactivate
方法中返回什么,以便它等待解决/拒绝承诺?
供参考,here is the DefinitelyTyped Promise definition。那里显然有决心和拒绝功能。
感谢您的帮助。
更新
这是更新的文件,其中Promise被初始化:
private promise: Promise<boolean> = new Promise(
( resolve: (res: boolean)=> void, reject: (res: boolean)=> void) => {
const res: boolean = false;
resolve(res);
}
);
和handleResponse
函数:
handleResponse(res: boolean) {
console.log('res: ', res);
this.promise.then(res => {
console.log('res: ', res);
});
}
它仍然无法正常工作,但模式显示并等待响应。当你说是离开时,它会留在组件上。此外,记录的第一个res
是从组件返回的正确值,但.then
函数内的函数与传递给handleResponse
函数的函数不同。 / p>
更多更新
在进行更多阅读后,似乎在promise
声明中,它设置了resolve
值,promise
无论如何都具有该值。因此,即使稍后我调用.then
方法,它也不会更改promise
的值,并且我无法将其设置为true并切换组件。有没有办法让promise
没有默认值,并且必须等到调用.then
方法?
更新的功能:
private promise: Promise<boolean> = new Promise((resolve, reject) => resolve(false) );
handleResponse(res: any) {
this.promise.then(val => {
val = res;
});
}
再次感谢您的帮助。
上次更新
在查看了许多建议之后,我决定创建一个Deferred
课程。它工作得很好,但当我执行deferred.reject(anyType)
时,我在控制台中收到错误:
EXCEPTION: Error: Uncaught (in promise): null
当我传递null
,string
或boolean
时,会发生同样的事情。试图在catch
类中提供Deferred
函数不起作用。
延期课程
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;
});
}
}
答案 0 :(得分:28)
我不熟悉bootstrap模态api,但我希望有一种方法可以在创建时以某种方式绑定到close事件。
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");
});
}
}
您正尝试使用Promise
Deferred
。如果您需要这种API,请自己写一个Deferred
类。
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);
}
}
}
答案 1 :(得分:2)
有没有理由说错误说Promise上没有'resolve'属性?
是的,而且tsc无法找到es6-promise
的正确输入。要避免在ng2项目中出现此问题和其他类型问题,as of beta.6您需要明确包含
///<reference path="node_modules/angular2/typings/browser.d.ts"/>
应用程序中的某个位置(通常在主引导程序文件的顶部完成)。*
你的问题的其余部分不太清楚(并且可能是XY problem,其中x是上面讨论的类型问题)。但是,如果我理解你已经定义了这样的承诺是正确的:
private promise: Promise<boolean> = new Promise(
( resolve: (res: boolean)=> void, reject: (res: boolean)=> void) => {
const res: boolean = false;
resolve(res); // <=== how would this ever resolve to anything but false???
}
);
你是如何期待这个解决任何错误的?
const res: boolean = false;
resolve(res); //always false
相当于
resolve(false); //always false
*注意:这(可能)是暂时的,在以后的测试版/发布版中不再需要。
更新以回应您的评论:
似乎不太明显我如何等待handleResponse函数运行并等待该响应
我仍然不清楚你在这里要做什么,但总的来说,你想让handleResponse
回复自己的承诺,然后:
private promise: Promise<boolean> = new Promise((resolve, reject) => {
handlePromise.then(resultOfHandleResult => {
//assuming you need to process this result somehow (otherwise this is redundant)
const res = doSomethingWith(resultOfHandleResult);
resolve(res);
})
});
handleResponse(res: any) {
this.promise.then(val => {
val = res;
});
}
或者,(远)更优选地,使用Observables:
var promise = handleResult() //returns an observable
.map(resultOfHandleResult => doSomethingWith(resultOfHandleResult))
答案 2 :(得分:2)
这是一项对我有用的技术。它与@ iliacholy的答案非常相似,但使用模态组件而不是jQuery模式。这使它更像一个角色2&#34;做法。我相信它仍与您的问题相关。
首先,为模态构建一个Angular Component:
import {Component, Output, EventEmitter} from '@angular/core;
@Component({
selector: 'myModal',
template: `<div class="myModal" [hidden]="showModal">
<!-- your modal HTML here... -->
<button type="button" class="btn" (click)="clickedYes()">Yes</button>
<button type="button" class="btn" (click)="clickedNo()">No</button>
</div>
`
})
export class MyModal{
private hideModal: boolean = true;
@Output() buttonResultEmitter = new EventEmitter();
constructor(){}
openModal(){
this.hideModal = false;
}
closeModal(){
this.hideModal = true;
}
clickedYes(){
this.buttonResultEmitter.emit(true);
}
clickedNo(){
this.buttonResultEmitter.emit(false);
}
}
然后在使用RouterCanDeactivate()的组件上,导入并引用MyModal实例:
import {MyModal} from './myModal';
@Component({
....
directives: [MyModal]
})
并在类代码中:
private myModal: MyModal;
创建一个返回promise的方法,该方法订阅了myModal上的eventEmitter:
userClick(): Promise<boolean> {
var prom: new Promise<boolean>((resolve, reject) => {
this.myModal.buttonResultEmitter.subscribe(
(result) => {
if (result == true){
resolve(true);
} else {
reject(false);
}
});
});
return prom;
}
最后,在RouterCanDeactivate钩子中:
routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
this.myModal.openModal();
return this.userClick().catch( function(){return false;} );
}
正如@drewmoore所提到的,使用Observables在Angular 2中是首选,但A)不是你的问题,而且B)routerCanDeactivate挂钩解析为boolean |承诺,所以这种方法对我来说似乎更自然。
答案 3 :(得分:0)
也可以使用Subject
s在Angular2 +世界中开箱即用:
export class MyComponent {
private subject: Subject<boolean>;
routerCanDeactivate(): PromiseLike<boolean> {
$('#modal').modal('show');
return this.subject.toPromise();
}
handleRespone(res: boolean) {
this.subject.next(res);
}
}