65.9K
CodeProject 正在变化。 阅读更多。
Home

Angular 2 JavaScript/ES5 教程 - 第 4 和 5 部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2016 年 3 月 5 日

MIT

7分钟阅读

viewsIcon

23081

downloadIcon

685

本文介绍“英雄之旅”Angular 2 教程的 JavaScript (ECMAScript 5) 版本。了解如何使用 JavaScript/ES5 实现 TypeScript 示例。本文涵盖了原教程的第 4 部分和第 5 部分。

为 Angular 2 (版本 2.0.0-beta7) 编写。 可能适用于其他版本。

引言

这是本系列的第二篇文章。在第一篇文章中,我们创建了一个带有两个组件的小型 Angular 2 应用程序。在本文中,我们将添加具有依赖注入和路由/导航的服务。

您可以在此处找到第一篇文章: Angular 2 JavaScript/ES5 教程 - 第 1 至 3 部分

使用代码

第 4 部分 - 依赖注入

第 4 部分最重要的方面是将一个服务通过依赖注入传递到我们的 AppComponent 中。

创建要注入的服务

创建服务

对于 HeroService,我们创建一个名为 hero.service.js 的单独 JavaScript 文件。我们将该对象注册为 app.HeroService,并从 constructor 开始。下面的代码片段已包含公共 getHeroes() 函数。

// HeroService stub (js)
(function (app) {
    app.HeroService = ng.core
        .Class({
            constructor: function () {
                this.getHeroes = function() {
                    return HEROES;
                };
            }
        });
})(window.app || (window.app = {}));
可注入装饰器

在 TypeScript 中,HeroService 会被 Injectable() 装饰。

// HeroService class (ts)
@Injectable()
    export class HeroService {
}

在转译后的 JavaScript 代码中,我们会找到一个对应的语句。但是,如果我们省略它,它似乎对类的行为没有影响。

模拟数组

HeroServicegetHeroes() 函数返回模拟数据 HEROES。遵循教程,我们将此数组从 app.js 移到其自己的文件 mock-heroes.js

在 index.html 中注册脚本

不要忘记在 index.html 中注册新的文件 hero.service.jsmock-heroes.js。脚本注册现在应该如下所示。

    <!-- script registration in index.html -->
    <script src='app/mock-heroes.js'></script>
    <script src='app/hero.service.js'></script>
    <script src='app/hero-detail.js'></script>
    <script src='app/app.js'></script>
    <script src='app/main.js'></script>

依赖注入

下一步,我们将 HeroService 注入到 AppComponent 中。为了在 Angular 2 中正确解析依赖项,我们需要执行两个步骤:

注册提供程序

我们将提供程序注册到组件。这对于 TypeScript 和 JavaScript 几乎相同。我们唯一需要更改的是在 providers 属性中直接指定如何从我们的 Angular 应用程序解析 app.HeroService

// Provider registration (js)
app.AppComponent = ng.core
    .Component({
        // ...
        providers: [ app.HeroService ]
    })
在构造函数中注入服务

这是最棘手的部分。在 TypeScript 中,我们可以通过提供以下详细信息,将提供的实例作为参数注入到构造函数中:

  • 访问修饰符
  • 变量名
  • 变量类型

在我们的例子中,它看起来像这样:

// AppComponent constructor (ts)
constructor(private _heroService: HeroService) { }

乍一看,此代码的 JavaScript 版本并不明显。我们不使用单个函数,而是将构造函数定义为一个数组。该数组包含我们想要注入的每个提供实例的类型,以及作为最后一个元素的构造函数。然后,我们可以将参数传递给构造函数,这些参数将按顺序作为提供的依赖项类型进行解析。

//AppComponent constructor (js)
constructor: [
    app.HeroService,
    function (heroService) {
        this.heroService = heroService;
    }
]

请注意,提供的类型和参数名称之间没有关系。如果我们以名为 foo 的构造函数参数注入 app.HeroService 类型,Angular 也完全可以。

使用注入的服务

如果 heroService 是一个公共属性,我们必须将其添加到对象中(例如 this.heroService = heroService);)。但是,由于我们将对象保持为私有,因此将在以下 getHeroes() 函数中使用构造函数参数 heroService

// getHeroes function in AppComponent (js)
this.getHeroes = function() {
    this.heroes = heroService.getHeroes();
};

现在应用程序将重新启动。正如您在右侧的屏幕截图中看到的,服务中的英雄将再次列在 AppComponent 中。

使用 Angular 生命周期钩子

当我们的组件经过某个“状态”(例如初始化或销毁)时,在组件中执行函数是一个常见用例。因此,Angular 提供了几个生命周期钩子 [^]。在这种情况下,我们想使用 ngOnInit 钩子来调用 getHeroes()

从 OnInit 派生

在 TypeScript 中,我们将实现 OnInit 接口,该接口提供一个 ngOnInit() 函数,Angular 将在相应的生命周期事件期间调用它。严格来说,这是一种继承。因此,我们将通过在对象的原型上提供正确的函数来在 JavaScript 中解决它。

//OnInit implementation in AppComponent (js)
app.HeroListComponent.prototype.ngOnInit = 
    function () {
        this.getHeroes();
    };

其他生命周期钩子,如 ngOnChangesngOnDestroy,可以使用相同的方式。

使用 Promise

第 4 部分的最后几个步骤涵盖了如何使用 Promise 处理异步结果。这是一个常用的实践,所以您应该仔细研究教程。这里只介绍 heroService 的更改后的函数。

//heroService (js)
this.getHeroes = function () {
    return new Promise(resolve =>
        setTimeout(() => resolve(HEROES), 2000)
    );
};

结果

完成这些步骤后,应用程序将从服务加载英雄。为了模拟远程服务器,响应会被延迟 2 秒。

第 5 部分 - 路由和导航

教程第 5 部分全部关于应用程序中的路由和导航。因此,我们使用 ng.router 包中的 Angular 2 默认路由器

添加推送 URL 基础

为了在 Web 浏览器中启用客户端导航,我们使用所谓的推送状态。有关此主题,请查看Angular 文档 [^] 和MDN 文档 [^]。在我们的应用程序中,我们在 index.html 中实现了一个 <base href="/"> 元素。

<!-- index.html -->
<head>
    <base href="/">

如果您在 Angular 2 应用程序中遇到以下异常,很可能是您缺少此行。

EXCEPTION: Error during instantiation of LocationStrategy! (RouterLink -> Router -> Location -> LocationStrategy).

配置路由器

路由器提供程序

在 Angular 2 中,我们需要一个组件来托管路由器。这将是我们的 AppComponent。为了告诉我们的应用程序如何解析路由器实例,我们引用路由器提供程序 ng.router.ROUTER_PROVIDERS

// AppComponent (js)
.Component({
    // ...
    providers: [ng.router.ROUTER_PROVIDERS, app.HeroService]
})
配置路由

接下来,我们可以通过指定 pathnamecomponent 的类型来配置我们的第一个路由到组件。前两个属性是字符串,最后一个是组件的类型(作为对象)。通过添加 useAsDefault 属性,我们告诉路由器在未选择任何路由时要解析哪个路由。要将路由添加到路由配置,我们会将其作为参数传递给 ng.router.RouterConfig()。通常我们会一次配置多个路由。因此,RouterConfig() 接受一个数组作为参数。然后我们用这个实例来装饰我们的 AppComponent

// AppComponent (js)
app.AppComponent = __decorate([
    ng.router.RouteConfig([ {
            path: '/heroes',
            name: 'Heroes',
            component: app.HeroesComponent,
            useAsDefault: true
        }])
    ],
    app.AppComponent);

代码示例中的 __decorate() 函数是我们从转译的 TypeScript 代码中获得的。不是最易读的部分,但它有效。

使用路由器

我们希望调用路由器链接以在路由状态之间导航。

注册路由器指令

要使用 HTML 中的路由器指令,我们首先需要引用路由器指令。

// AppComponent (js)
.Component({
    // ...
    directives: [ng.router.ROUTER_DIRECTIVES],
    providers: [ng.router.ROUTER_PROVIDERS, app.HeroService]
})
路由器链接和路由器出口

在 JavaScript 中实现这一点后,我们可以在 HTML 中简单地添加 routerLink<router-outlet />

<!-- app.html -->
<a [routerLink]="['Heroes']">Heroes</a>
<router-outlet>Heroes</router-outlet>

结果

现在启动应用程序,我们会看到路由器已经导航到我们的英雄组件。请注意浏览器 URL 如何变化。但是,heroes 路由是我们拥有的唯一路由,没有可导航的内容。我们将在下一部分中对此进行更改。

完成教程

添加路由器和配置路由是本部分中最艰巨的任务。现在我们添加一个名为 dashboard 的新组件,并设置 dashboardhero listhero detail 视图之间的路由。如果您坚持使用原始的 TypeScript 教程,用 JavaScript 实现这一点应该不会太难。您可以在本文中找到我为第 5 部分提供的 JavaScript 解决方案。

结论

通过一点努力,就可以仅使用 JavaScript / ECMAScript 5 设置此应用程序。对于 Angular 2 的初步尝试,没有必要深入研究 TypeScript,尽管为了理解文档和提示,确实需要一些 TypeScript 知识。如果我事先知道这些步骤,我就可以节省大量研究、调试和尝试的时间。

您在 Angular 2 方面的首次体验如何,特别是与 JavaScript/ES5 结合使用时?您尝试过不同的方法吗?您是否找到了某些方面的更简单解决方案,或者我遗漏了什么?请在下面的评论中告诉我。

历史

  • 2016年3月5日:初始版本

许可证

原始教程,特别是源代码文件,已根据 MIT 风格的许可证发布,该许可证可在 http://angular.io/license 的 LICENSE 文件中找到。本教程和源代码修改根据 MIT 许可证发布,该许可证可在 https://open-source.org.cn/licenses/MIT 中找到。

© . All rights reserved.