【问题标题】:Injectables in Angular2Angular2 中的注射剂
【发布时间】:2015-05-18 19:38:38
【问题描述】:

我在玩 Angular2。作为基础,我使用了angular.io 页面中的quickstart 项目。

一切似乎都运行良好,但是一旦我尝试将服务 (ItemService) 注入我的 AppComponent 中,就会出现以下异常:

在 Token(ComponentRef) 实例化期间出错!。原始错误:无法解析 AppComponent 的所有参数。确保它们都有有效的类型或注释。

我在互联网上看到过类似的问题(包括stackoverflow,例如this post),但似乎都没有解决我的问题。有没有人知道问题可能是什么?

我还看到了一些解决方案(例如 Angular2 存储库中的解决方案),它们使用 Injectable-annotation 装饰可注入类。然而这对我不起作用,因为它没有在angular.d.ts 中定义。是不是我用错版本了?

您可以在以下 Plunker 中找到我的解决方案: http://plnkr.co/edit/7kK1BtcuEHjaspwLTmsg

作为记录,您还可以在下面找到我的两个文件。请注意,Plunker 中的app.js 是我下面的 TypeScript 文件生成的 JavaScript 文件,异常总是相同的。

index.html:

<html>
    <head>
        <title>Testing Angular2</title>
        <script src="https://github.jspm.io/jmcriffey/bower-traceur-runtime@0.0.87/traceur-runtime.js"></script>
        <script src="https://jspm.io/system@0.16.js"></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);

提前感谢您的任何想法!

【问题讨论】:

    标签: angular typescript


    【解决方案1】:

    检查几件事:-

    1) 确保您使用的是从您的软件包位置安装的正确版本的 typescript (1.5.x)。 () 如果您安装了以前版本的 typescript,则仅使用“tsc”命令可能会从环境路径指向现有 (

    2) 确保在 tsc 命令中使用--emitDecoratorMetadata 标志。这是必要的,因此 javascript 输出会为装饰器创建元数据。

    3) Error - Cannot read property 'annotations' of undefined 因为AppComponent 指令对DetailComponent 的依赖尚未定义(当同步__decorate( 函数运行以解决依赖关系时)并且TS 编译提升变量DetailComponent 的方式是未定义的(下面的 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 开始,如果您在遵循当前文档(截至目前)follow this link if it helps 方面遇到问题,因为有一些相关更改。

    【讨论】:

    • 还注意到你有 ../../ 打字,如果打字是在根文件夹之外有效,但如果它在你的根目录内,你可能想要删除一个 ../
    • 感谢您的回复。将DetailComponent 的定义移到AppComponent 之前并没有改变任何东西。如果我不使用ItemService 而是定义Items 内联,它就可以工作,例如在AppComponent 的构造函数中。它确实与injectables 有关。打字在根文件夹之外,所以我想这也不应该是一个问题..
    • @PzYon 事实上,当我移动它时,它对我来说效果很好。您确定您的文件是否正确编译?我也在使用 v23.a。我现在无法访问 plunker,所以无法真正看到发生了什么......
    • 感谢您的意见。恐怕,我不是 TypeScript 方面的专家,但据我所知,它(重新)编译正确,并且在我的情况下顺序无关紧要。我在从 WebStorm 中执行的 Windows 上使用 TypeScript 1.5 beta。我将以下选项传递给 TypeScript 编译器:--module "commonjs" --target es5。如果我另外通过--emitDecoratorMetadata(如 angular.io 快速入门中所述),我会得到Error: Cannot start compiler process。也许这就是原因.. 如果我将编译的 .js 文件与您的文件进行比较,我可以看到我缺少 __metadata-lines。
    • @PzYon 这是问题的一部分。事实上,我从来没有得到你在问题中提到的错误,这似乎与编译器有关。但是,一旦您解决了这个问题,您最终可能会得到Cannot read property 'annotations' of undefined,因为您尝试注入稍后定义的服务,打字稿仅转换为javascript,这意味着它将您稍后在文件中定义的 DetailComponent 类转换为将其分配给变量的函数调用 IIFE
    【解决方案2】:

    我的设置:我在 Windows 上运行 WebStorm 10.0.2,使用 TypeScript 1.5 beta(编译为 ES5)和 node/npm。

    感谢 PSL 的帮助,我发现我的设置存在一些问题。 它们与代码无关,例如不要按照类定义的顺序等。 只是为了记录,我想总结一下我遇到的问题。PSL 已经提到了大多数问题,但也许其他人也有同样的问题,例如与WebStorm中的编译器有关。

    这是我遇到的问题:

    • 尽管我将 WebStorm 配置为使用通过 npm 安装的 TypeScript 1.5,但还是使用了旧版本 (1.3),因为它也安装在我的系统上(我认为是与 Visual Studio 一起安装的)并在 @987654322 中注册@变量

    • 我将 WebStorm 配置为使用 TypeScript 的“自定义编译器版本”(在设置/语言和框架/TypeScript 中),具有以下参数:-emitDecoratorMetadata -m commonjs -t es5 -sourceMap。这有些不起作用..我不确定它是否与 WebStorm 中的问题有关。如果没有 -emitDecoratorMetadata 标志,它会编译,但 injectables 不起作用。

      [2015 年 6 月 9 日更新:是的,它与 WebStorm 中的 issue 相关]

    • 但是,我需要 -emitDecoratorMetadata-flag 才能使 Angular2 中的 injectables 工作。

    • 我的解决方案是在 WebStorm 中添加一个自定义“文件观察器”(而不是使用“内置编译器”功能),并带有以下参数:-emitDecoratorMetadata -m commonjs -t es5 --sourceMap $FilePath$

    使用 Angular2、TypeScript 1.5 和 WebStorm 的经验教训:

    • 确保 TypeScript 1.5 确实是用于编译 ts 文件的版本(例如在您的 IDE 中)。

    • 确保指定-emitDecoratorMetadata

    • 没有区别,如果类是按特定顺序或在自己的文件中定义的,等等。 -> 顺序很重要!将类/组件拆分为不同的文件可以解决问题,并且通常会导致更好的可维护文件。

    再次感谢 PSL 的宝贵意见!

    【讨论】:

    • 不错的答案。但我无法理解在您最初的问题中,您在定义之前注入 DetailsComponent 的方式......它应该给出错误Cannot read property 'annotations' of undefined,这很有意义。因为directives: [angular2_1.For, angular2_1.If, DetailComponent] 行在var DetailComponent = (function () { 之前运行,这意味着包含定义的提升变量DetailComponent 将是未定义的,而且__decorate 也不是异步的。定义自己的文件并导入它当然就可以了。
    • 是的,你当然是对的:订单确实很重要,如果我在我想注入的组件后面有我的可注入组件 (DetailComponent),我会遇到同样的例外情况它(AppComponent)。我相应地更新了我的答案。很抱歉造成混乱。
    • 感谢您的确认...我无法理解您为什么不... :)
    【解决方案3】:

    我也遇到过同样的问题。以下是决议:

    1. --emitDecoratorMetadata 在 tsc 中丢失
    2. 如果我从同一个文件注入服务,它会失败,但是当我将服务放入其他文件时,当导入该文件时,它开始工作。可能是我使用的 Angular2 版本中的错误。

    【讨论】:

      猜你喜欢
      • 2011-10-29
      • 1970-01-01
      • 2018-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-08
      • 1970-01-01
      • 2012-07-21
      相关资源
      最近更新 更多