world versiones starter hello guide app typescript angular

typescript - versiones - install angular



Angular 2 y promesas de TypeScript (5)

Aquí hay una técnica que funcionó para mí. Es bastante similar a la respuesta de @ iliacholy, pero usa un componente modal en lugar de un modal jQuery. Esto lo hace un enfoque algo más "angular 2". Creo que sigue siendo relevante para tu pregunta.

Primero, construya un componente angular para el modal:

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

Luego, en su componente con RouterCanDeactivate (), importe y haga referencia a la instancia de MyModal:

import {MyModal} from ''./myModal''; @Component({ .... directives: [MyModal] })

y en el código de clase:

private myModal: MyModal;

Cree un método que devuelva una promesa, que está suscrito al eventEmitter en 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; }

y finalmente, en el enrutador RouterCanDeactivate:

routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) { this.myModal.openModal(); return this.userClick().catch( function(){return false;} ); }

Como se menciona en @drewmoore, el uso de Observables se prefiere en Angular 2, pero A) esa no era su pregunta, y B) El enrutadorCanDeactivate se resuelve en booleano | Promesa, por lo que este enfoque me pareció más natural.

Estoy intentando usar la función routerCanDeactivate para un componente en mi aplicación. La forma más sencilla de usarlo es la siguiente:

routerCanDeactivate() { return confirm(''Are you sure you want to leave this screen?''); }

Mi único problema con esto es que es feo. Solo usa un indicador de confirmación generado por el navegador. Realmente quiero usar un modal personalizado, como un modal Bootstrap. Tengo el modo Bootstrap que devuelve un valor verdadero o falso en función del botón en el que hacen clic. El routerCanDeactivate que estoy implementando puede aceptar un valor verdadero / falso o una promesa que se resuelva en verdadero / falso.

Aquí está el código para el componente que tiene el método 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); } } }

Cuando compilan mis archivos TypeScript, obtengo los siguientes errores en la terminal:

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

Cuando trato de dejar el componente, el modal se inicia, pero luego el componente se desactiva y no espera la promesa de resolución.

Mi problema es tratar de resolver la Promesa para que el método routerCanDeactivate espere la promesa de resolver. ¿Hay alguna razón por la cual hay un error al decir que no existe una propiedad ''resolve'' en Promise<boolean> ? Si puedo trabajar con esa parte, ¿qué debo devolver en el método routerCanDeactivate para que espere la resolución / rechazo de la promesa?

Como referencia, aquí está la definición de Promesa Definitivamente Tipada . Claramente hay una función de resolución y rechazo allí.

Gracias por tu ayuda.

ACTUALIZAR

Aquí está el archivo actualizado, con la Promesa inicializada:

private promise: Promise<boolean> = new Promise( ( resolve: (res: boolean)=> void, reject: (res: boolean)=> void) => { const res: boolean = false; resolve(res); } );

y la función handleResponse :

handleResponse(res: boolean) { console.log(''res: '', res); this.promise.then(res => { console.log(''res: '', res); }); }

Todavía no funciona correctamente, pero el modal aparece y espera la respuesta. Cuando dice que sí, se queda en el componente. Además, la primera res que se registra es el valor correcto devuelto por el componente, pero la función dentro de .then no es la misma que la que se handleResponse a la función handleResponse .

Más actualizaciones

Después de leer un poco más, parece que en la declaración de promise establece el valor de resolve , y la promise tiene ese valor sin importar qué. Entonces, aunque más tarde llame al método .then , no cambia el valor de la promise y no puedo hacerlo verdadero y cambiar los componentes. ¿Hay alguna manera de hacer que la promise no tenga un valor predeterminado y que tenga que esperar hasta que se .then método its .then ?

Funciones actualizadas

private promise: Promise<boolean> = new Promise((resolve, reject) => resolve(false) ); handleResponse(res: any) { this.promise.then(val => { val = res; }); }

Gracias de nuevo por la ayuda.

Última actualización

Después de ver muchas sugerencias, decidí crear una clase Deferred . Funcionó bastante bien, pero cuando hago el deferred.reject(anyType) , deferred.reject(anyType) un error en la consola de:

EXCEPTION: Error: Uncaught (in promise): null

Esto mismo ocurre cuando paso null , una string o un boolean . Intentar proporcionar una función catch en la clase Deferred no funcionó.

Clase diferida

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


Como todo el mundo habla de Observables, pensé que echaría un vistazo y construiría sobre la respuesta de @ petryuno1.

Comenzando con su 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); } }

A continuación, vamos al 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(); }); } }

La elegancia de los observables es que ahora podemos escribir mucho menos código para hacer lo mismo.


No estoy familiarizado con la API modal de arranque, pero espero que haya una forma de vincularse a un evento cercano de alguna manera al crearlo.

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

Está intentando usar la Promise como Deferred . Si quieres ese tipo de API, escríbete una clase 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); } } }


¿Hay alguna razón por la cual hay un error al decir que no hay una propiedad ''resolver'' en Promise?

Sí, y es que tsc no puede encontrar los typings correctos para es6-promise . Para evitar este y otros problemas de tipeo en proyectos ng2, a partir de la beta.6 debes incluir explícitamente

///<reference path="node_modules/angular2/typings/browser.d.ts"/>

en algún lugar de su aplicación (generalmente esto se hace en la parte superior del archivo principal de arranque). *

El resto de su pregunta es menos clara (y es probable que sea un problema XY donde x es el problema de tipings discutido anteriormente). Pero, si estoy en lo cierto al entender que has definido una promesa como esta:

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

¿Cómo esperas que esto se resuelva con algo que no sea falso?

const res: boolean = false; resolve(res); //always false

es equivalente a

resolve(false); //always false

* nota: esto es (presumiblemente) temporal, y no será necesario en versiones beta / versión posteriores.

Actualiza en respuesta a tu comentario:

no parece obvio cómo puedo esperar a que se ejecute la función handleResponse y esperar esa respuesta

Todavía no tengo claro qué estás tratando de hacer aquí, pero en general, querrás que handleResponse devuelva una promesa propia, y luego:

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

O, ( lejos ) más preferiblemente, use Observables:

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


También se puede hacer de forma inmediata en el mundo Angular2 + utilizando Subject s:

export class MyComponent { private subject: Subject<boolean>; routerCanDeactivate(): PromiseLike<boolean> { $(''#modal'').modal(''show''); return this.subject.toPromise(); } handleRespone(res: boolean) { this.subject.next(res); } }