angular - Inyectables en Angular2
Estoy jugando con Angular2. Como base, utilicé el proyecto de inicio rápido de la página angular.io.
Todo parece funcionar bien, pero tan pronto como intento inyectar un servicio (ItemService) en mi AppComponent obtengo la siguiente excepción:
Error durante la instanciación de Token(ComponentRef)!. ERROR ORIGINAL: No se pueden resolver todos los parámetros para AppComponent. Asegúrese de que todos tengan un tipo válido o anotaciones.
He visto problemas similares en Internet (incluyendo StackOverflow, por ejemplo esta publicación), pero ninguno de ellos parece resolver mi problema. ¿Alguien tiene alguna idea de cuál podría ser el problema?
También he visto algunas soluciones (por ejemplo las del repositorio de Angular2) que decoran la clase inyectable con la anotación Injectable. Sin embargo, esto no funciona para mí, ya que no está definido en angular.d.ts. ¿Estoy usando una versión incorrecta?
Puedes encontrar mi solución en el siguiente Plunker: http://plnkr.co/edit/7kK1BtcuEHjaspwLTmsg
Para que conste, también puedes encontrar mis dos archivos a continuación. Por favor, ten en cuenta que el app.js del Plunker es el archivo JavaScript generado a partir de mi archivo TypeScript a continuación, la excepción siempre es la misma.
index.html:
<html>
<head>
<title>Testing Angular2</title>
<script src="https://github.jspm.io/jmcriffey/[email /traceur-runtime.js"></script>
<script src="https://jspm.io/[email "></script>
<script src="https://code.angularjs.org/2.0.0-alpha.23/angular2.dev.js"></script>
</head>
<body>
<app></app>
<script>
System.import('js/app');
</script>
</body>
</html>
js/app.ts:
/// <reference path="../../typings/angular2/angular2.d.ts" />
import {Component, View, bootstrap, For, If} from "angular2/angular2";
class Item {
id:number;
name:string;
constructor(id:number, name:string) {
this.id = id;
this.name = name;
}
}
class ItemService {
getItems() {
return [
new Item(1, "Bill"),
new Item(2, "Bob"),
new Item(3, "Fred")
];
}
}
@Component({
selector: 'app',
injectables: [ItemService]
})
@View({
template: `<h1>Testing Angular2</h1>
<ul>
<li *for="#item of items">
{{item.id}}: {{item.name}} |
<a href="javascript:void(0);" (click)="toggleSelected(item);">
{{selectedItem == item ? "unselect" : "select"}}
</a>
</li>
</ul>
<item-details *if="selectedItem" [item]="selectedItem"></item-details>`,
directives: [For, If, DetailComponent]
})
class AppComponent {
items:Item[];
selectedItem:Item;
constructor(itemService:ItemService) {
this.items = itemService.getItems();
}
toggleSelected(item) {
this.selectedItem = this.selectedItem == item ? null : item;
}
}
@Component({
selector: 'item-details',
properties: {
item: "item"
}
})
@View({
template: `<span>You selected {{item.name}}</span>`
})
class DetailComponent {
item:Item;
}
bootstrap(AppComponent);
¡Gracias de antemano por cualquier idea!
Respuesta #1
Revisa un par de cosas:-
1) Asegúrate de que estás utilizando la versión correcta de TypeScript (1.5.x) instalada desde la ubicación de tu paquete.() Si tienes versiones anteriores de TypeScript instaladas, simplemente usar el comando "tsc" podría estar apuntando a la ubicación existente (< 1.5) desde la ruta del entorno.
2) Asegúrate de usar la bandera --emitDecoratorMetadata con el comando tsc. Es necesario para que la salida de javascript cree los metadatos para los decoradores.
3) Error - No se puede leer la propiedad 'annotations' de undefined Porque la directiva AppComponent tiene una dependencia en DetailComponent que aún no ha sido definida (para cuando la función síncrona __decorate( se ejecuta para resolver las dependencias) y la forma en que TS compila la variable elevada DetailComponent es undefined en ese momento (el IIFE correspondiente aún no se ha ejecutado). Intenta mover la declaración de DetailComponent antes de AppComponent. O simplemente muévelo a un archivo diferente e impórtalo.
@Component({
selector: 'item-details',
properties: {
item: "item"
}
})
@View({
template: `<span>You selected {{item.name}}</span>`
})
class DetailComponent {
item:Item;
}
@Component({
selector: 'app',
injectables: [ItemService]
})
@View({
template: `<h1>Testing Angular2</h1>
<ul>
<li *for="#item of items">
{{item.id}}: {{item.name}} |
<a href="#" (click)="toggleSelected(item);">
{{selectedItem == item ? "unselect" : "select"}}
</a>
</li>
</ul>
<item-details *if="selectedItem" [item]="selectedItem"></item-details>`,
directives: [For, If, DetailComponent]
})
class AppComponent {
items:Item[];
selectedItem:Item;
constructor(itemService:ItemService) {
this.items = itemService.getItems();
}
toggleSelected(item) {
this.selectedItem = this.selectedItem == item ? null : item;
return false;
}
}
bootstrap(AppComponent);
Editar
A partir de Angular a26, si enfrentas problemas con respecto a seguir la documentación actual (hasta ahora), sigue este enlace si ayuda, ya que hay algunos cambios relevantes.
Respuesta #2
Mi configuración: Estoy usando WebStorm 10.0.2 en Windows, utilizando TypeScript 1.5 beta (compilado a ES5) y node/npm.
Gracias a la ayuda de PSL, encontré varios problemas con mi configuración. Solo para que conste, me gustaría resumir los problemas que tuve. La mayoría de ellos han sido mencionados por PSL, pero tal vez otras personas tengan los mismos problemas, por ejemplo, relacionados con el compilador en WebStorm.
Aquí están los problemas que tuve:
Aunque configuré WebStorm para usar TypeScript 1.5 instalado a través de npm, se utilizó una versión antigua (1.3), ya que esta también estaba instalada en mi sistema (creo que junto con Visual Studio) y registrada en la variable
%path%Configuré WebStorm para usar una "Versión de Compilador Personalizada" para TypeScript (en configuración/Idiomas y Frameworks/TypeScript), con los siguientes argumentos:
-emitDecoratorMetadata -m commonjs -t es5 -sourceMap. De alguna manera esto no funciona... No estoy seguro si está relacionado con un problema en WebStorm. Sin la bandera-emitDecoratorMetadatase compiló, pero losinjectablesno funcionaron.[Actualización, 9 de junio de 2015: Sí, está relacionado con un problema en WebStorm]
Sin embargo, necesito la opción
-emitDecoratorMetadatapara que losinjectablesen Angular2 funcionen.Mi solución fue agregar un "File Watcher" personalizado en WebStorm (en lugar de usar la función de "Compilador incorporado") con los siguientes argumentos:
-emitDecoratorMetadata -m commonjs -t es5 --sourceMap $FilePath$
Lecciones aprendidas al trabajar con Angular2, TypeScript 1.5 y WebStorm:
Asegúrate de que TypeScript 1.5 sea realmente la versión que se utiliza para compilar los archivos ts (por ejemplo, en tu IDE).
Asegúrese de que se especifique
-emitDecoratorMetadata.No importa si las clases se definen en un orden específico o en archivos separados, etc.-> ¡El orden sí importa! Dividir las clases/componentes en archivos diferentes resuelve el problema y, en general, conduce a archivos más fáciles de mantener.
¡Gracias de nuevo a PSL por su valiosa contribución!
