2016-04-13 149 views
21

我正在尝试在我的应用中为组件使用routerCanDeactivate函数。使用它的简单方法如下:Angular 2和TypeScript Promises

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); 
     } 
    } 
} 

当我的打字稿文件编译,我得到的终端以下错误:

error TS2339: Property 'resolve' does not exist on type 'Promise<boolean>'. 
error TS2339: Property 'reject' does not exist on type 'Promise<boolean>'. 

当我尝试离开组件,模式开始,但是组件停用并且不等待解决的承诺。

我的问题是试图找出承诺,以便routerCanDeactivate方法等待承诺解决。是否有理由说Promise<boolean>上没有'resolve'财产?如果我能解决这个问题,那么我必须在routerCanDeactivate方法中返回什么,以便等待解决方案/拒绝承诺?

仅供参考,here is the DefinitelyTyped Promise definition。那里显然有决心和拒绝功能。

感谢您的帮助。

UPDATE

下面是更新后的文件,与无极初始化:

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函数的函数不同。

更多更新

做一些更多的阅读后,似乎在promise声明,它集resolve值,promise有值不管。所以即使后来我调用.then方法,它不会更改promise的值,我不能使其成为真实并切换组件。有没有办法使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。尝试在Deferred类中提供catch函数不起作用。

递延类

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; 
     }); 
    } 
} 
+3

我可能是错的,因为我不知道打字稿,但你不应该先初始化* *'这一点。 promise'?更准确地说,你不应该返回一个承诺,并继续参考它的“决定”和“拒绝”功能,然后调用它们吗? – Amit

+0

@提醒你是正确的,我更新了原文。 – pjlamb12

+0

什么是调用句柄响应? –

回答

24

我不熟悉的自举模式的API,但是我希望那里是创建时,它绑定到接近事件莫名其妙的方式。

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"); 
    }); 
    } 

} 

你试图使用PromiseDeferred。如果你想要这种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); 
     } 
    } 
} 
+0

感谢您的建议!这真的很接近工作。由于某种原因延迟的拒绝错误,如果你用错误来解决,它也会让人感到怪异,所以我只需要弄清楚它为什么会这样做。 – pjlamb12

+0

我终于在我的项目中回到了这个问题上,并且这个作品效果最好。在我尝试过之前制作延迟类并没有工作,但是当我使用了关于在模式关闭时监听事件的最高建议时,该部分为我工作。谢谢! – pjlamb12

2

是否有一个原因,为什么有一个错误,指出存在的承诺没有“决心”属性?

是的,它是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; 
    }); 
} 

或者, ()更优选,使用观测量:

var promise = handleResult() //returns an observable 
       .map(resultOfHandleResult => doSomethingWith(resultOfHandleResult)) 
+0

因此,当我开始进行更多挖掘时,我意识到我将它设置为始终为假,但似乎并不明显,我如何等待'handleResponse'函数运行,并在解决诺言之前等待该响应。此外,我认为错误来自于未正确初始化承诺实例,我相信我已修复此实例。 – pjlamb12

+0

@ pjlamb12请参阅编辑...是你在找什么? – drewmoore

+0

我确实需要一种方法来使'routerCanDeactivate'方法等待根据'handleResult'方法解析为true或false,我只是不确定最好的方法去解决它。上面的“Deferred”类的答案几乎可行,但当承诺被解析为false或拒绝时会出错,所以结果不起作用。 – pjlamb12

2

这是一种为我工作的技术。它与@ iliacholy的答案非常相似,但是使用了模式组件而不是jQuery模式。这使得它更像“Angular 2”方法。我相信它仍然与你的问题有关。

首先,建立一个角度分量的模态:

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; 

创建一个方法返回一个约定,该约定订阅了事件E米特上myModal:

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提到的,使用观测量是优选的角2,但A),这不是你的问题和B)routerCanDeactivate挂接解析为布尔|承诺,所以这种方法对我来说似乎更自然。

+0

感谢您的评论我一定会尝试这一点,并回到它与它如何为我工作。 – pjlamb12

+0

这是一个梦幻般的回应。我不得不修改'userClick()'函数来记住订阅,以便我可以取消订阅。 'this.subscription = this.myModal.buttonResultEmitter.subscribe(...)'。同样在'routerCanDeactivate'中,我需要一段'this.userClick()。then(result => {console.log(result); this.subscription.unsubscribe();})' – Jon

2

由于每个人都在谈论Observables,我想我会看一看,并建立在@ petryuno1的答案。

与他ModalComponent开始:

import {Component, Output, ViewChild} from '@angular/core; 
@Component({ 
    selector: 'myModal', 
    template: `<div class="myModal" [hidden]="showModal"> 
      <!-- your modal HTML here... --> 
      <button type="button" class="btn" (click)="clickedYes($event)">Yes</button> 
      <button type="button" class="btn" (click)="clickedNo($event)">No</button> 
     </div> 
    ` 
}) 

export class MyModal{ 
    private hideModal: boolean = true; 
    private clickStream = new Subject<boolean>(); 
    @Output() observable = this.clickStream.asObservable(); 

    constructor(){} 
    openModal(){ 
     this.hideModal = false; 
    } 
    closeModal(){ 
     this.hideModal = true; 
    } 
    clickedYes(){ 
     this.clickStream.next(true); 
    } 
    clickedNo(){ 
     this.clickStream.next(false); 
    } 
} 

接下来,我们去AppComponent

import { Component, ViewChild} from '@angular/core'; 
import {MyModal} from './myModal'; 
import {Subscription} from "rxjs"; 

@Component({ 
    .... 
    directives: [MyModal] 
}) 

export class AppComponent { 
    @ViewChild(ConfirmModal) confirmModal: ConfirmModal; 
    constructor(){...}; 

    public showModal(){ 
     this.myModal.openModal(); 
     this.subscription = this.myModal.observable.subscribe(x => { 
      console.log('observable called ' + x) 
// unsubscribe is necessary such that the observable doesn't keep racking up listeners 
      this.subscription.unsubscribe(); 
     }); 
    } 
} 

观测的优雅是,我们现在去写少了很多代码做同样的事情。

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); 
    } 
}