Angular 2和TypeScript Promises

时间:2016-04-13 21:35:01

标签: typescript angular

我正在尝试将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

当我传递nullstringboolean时,会发生同样的事情。试图在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;
        });
    }
}

4 个答案:

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