Инъекции в Angular2

Я играю с Angular2. В качестве основы я использовал проект quickstart из angular.io.

Все работает нормально, но как только я пытаюсь ввести службу (ItemService) в мой AppComponent, я получаю следующее исключение:

Ошибка при создании Token (ComponentRef)!. ОРИГИНАЛЬНАЯ ОШИБКА. Невозможно разрешить все параметры для AppComponent. Убедитесь, что все они имеют допустимый тип или аннотации.

Я видел похожие проблемы в Интернете (включая stackoverflow, например этот пост), но ни один из них, похоже, не решил мою проблему. Есть ли какие-либо идеи, какая проблема может быть?

Я также видел некоторые решения (например, те, что были в репо Angular2), которые украшают класс для инъекций с помощью Injectable -nnotation. Однако это не работает для меня, поскольку это не определено в angular.d.ts. Я использую неправильную версию?

Вы можете найти мое решение в следующем Plunker: http://plnkr.co/edit/7kK1BtcuEHjaspwLTmsg

Для записи вы также можете найти мои два файла ниже. Обратите внимание, что app.js из Plunker - это файл JavaScript, созданный из моего файла TypeScript ниже, исключение всегда одно и то же.

index.html:

<html>
    <head>
        <title>Testing Angular2</title>
        <script src="https://github.jspm.io/jmcriffey/[email protected]/traceur-runtime.js"></script>
        <script src="https://jspm.io/[email protected]"></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);

Заранее благодарим за любые идеи!

Ответ 1

Проверьте несколько вещей: -

1) Убедитесь, что вы используете правильную версию typescript (1.5.x), установленную из вашего местоположения вашего пакета.() Если у вас установлены предыдущие версии typescript, то просто использование команды tsc может указывать на существующее (< 1,5) местоположение из пути к среде.

2) Убедитесь, что вы используете флаг --emitDecoratorMetadata с командой tsc. Это необходимо, чтобы выход javascript создавал метаданные для декораторов.

3) Error - Cannot read property 'annotations' of undefined  Поскольку директива AppComponent имеет зависимость от DetailComponent, которая еще не определена (к моменту выполнения синхронной функции __decorate( для разрешения зависимостей) и способ, которым TS компилирует вставленную переменную DetailComponent равен undefined, тогда (IIFE для этого ниже еще предстоит запустить). Попробуйте переместить объявление DetailComponent до AppComponent. Или просто переместите его в другой файл и импортируйте его.

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

Изменить

По состоянию на angular a26, если вы столкнулись с проблемами в отношении выполнения текущей документации (на данный момент) следуйте этой ссылке, если это помогает, поскольку там некоторые соответствующие изменения.

Ответ 2

Моя настройка: я запускаю WebStorm 10.0.2 в Windows, используя TypeScript 1.5 beta (скомпилированный в ES5) и node/npm.

Благодаря помощи PSL я обнаружил несколько проблем с моей настройкой. Они не были связаны с кодом, например. а не по порядку определений классов и т.д. Только для записи я хотел бы обобщить проблемы, которые у меня были. Большинство из них упоминалось в PSL, но, возможно, у других людей есть те же проблемы, например. связанные с компилятором в WebStorm.

Вот проблемы, которые у меня были:

  • Несмотря на то, что я настроил WebStorm на использование TypeScript 1.5, установленного через npm, использовалась старая версия (1.3), поскольку она также была установлена ​​в моей системе (я думаю, вместе с Visual Studio) и зарегистрирована в %path% variable

  • Я настроил WebStorm на использование "Custom Compiler Version" для TypeScript (в настройках/Языки и Frameworks/TypeScript) со следующими аргументами: -emitDecoratorMetadata -m commonjs -t es5 -sourceMap. Некоторые из них не работают. Я не уверен, если это связано с проблемой в WebStorm. Без флага -emitDecoratorMetadata он скомпилирован, но injectables не работает.

    [Обновление, 9. Июнь 2015: Да, это связано с issue в WebStorm]

  • Однако мне нужен -emitDecoratorMetadata -flag, чтобы injectables в Angular2 работал.

  • Моим решением было добавить пользовательский "Наблюдатель файлов" в WebStorm (вместо использования встроенной функции компилятора) со следующими аргументами: -emitDecoratorMetadata -m commonjs -t es5 --sourceMap $FilePath$

Уроки, извлеченные при работе с Angular2, TypeScript 1.5 и WebStorm:

  • Убедитесь, что TypeScript 1.5 действительно версия, которая используется для компиляции файлов ts (например, в вашей среде IDE).

  • Убедитесь, что указан -emitDecoratorMetadata.

  • Это не имеет значения, если классы определены в определенном порядке или в собственных файлах и т.д. → Заказ имеет значение! Разделение классов/компонентов на разные файлы решает проблему и, как правило, приводит к лучшим файлам, поддерживающим maintanable.

Еще раз спасибо PSL за его ценный вклад!

Ответ 3

У меня была та же проблема. Ниже была резолюция:

  • - emitDecoratorMetadata был пропущен в tsc
  • Если я вводил службу из того же файла, она терпела неудачу, но когда я помещал эту услугу в какой-то другой файл, и когда она была импортирована, она начала работать. вероятно, ошибка в версии Angular2, которую я использовал.