Dart和Angular2的英雄之旅(4):多个组件

我们的应用成长了不少。现在又有新的任务:重复使用组件,传递数据给组件,并创建更多可复用的资源。 我们来把英雄详情从英雄列表中拆分出来,让这个英雄详情组件可以被复用。

运行这部分的 在线实例下载源码

我们离开的地方

在继续《英雄之旅》之前,先来检查一下是否有以下目录结构。如果不是,你得回到上一章,并看看错过了什么。

保持应用的编译和运行

我们需要启动Dart编译器,它会监视文件变更,并且启动服务器。我们只要敲入:

pub serve

在我们继续构建《英雄之旅》的时候,这个命令会让应用得以持续运行。

制作英雄详细组件

我们的英雄列表和英雄详情目前位于同一个文件的同一个组件中。现在它们还很小,但很快它们都会变大。我们将来肯定会收到新需求:针对这一个,却不能影响另一个。然而,每一个更改都会给这两个组件带来风险,并且带来双倍的测试负担,却没有任何好处。如果我们不得不在本应用之外复用英雄详情组件,那么英雄列表组件也会跟着混进去。

目前,我们这个组件违反了单一职责原则(Single Responsibility Principle)。虽然这只是一个教程,但我们还是得坚持做正确的事——做正确的事这么容易,我们何乐而不为呢?况且,我们学习的就是如何解决构建 Angular 应用过程中的问题。

让我们把英雄详情拆分成一个独立的组件。

拆分英雄详情组件

lib 目录下添加一个名叫 hero_detail_component.dart 的文件,并且创建 HeroDetailComponent 。代码如下:

import 'package:angular2/core.dart';

@Component(
    selector: 'my-hero-detail',
    )
class HeroDetailComponent {
}

当我们创建组件的时候,一开始要导入 Angular 的 core.dart 文件,以便能使用 @Component 等通用类型。

我们使用注解 @Component 创建元数据,在其中指定选择器名称,以标记此组件对应的元素。

做完这些,我们把它导入 AppComponent 组件,并创建相应的 <my-hero-detail> 元素。

英雄详情模板

目前, AppComponent 的“英雄列表”和“英雄详情”视图被组合在同一个模板中。让我们从 AppComponent 中剪切英雄详情的内容,并且粘贴到 HeroDetailComponent 组件的 template 属性中。

以前我们绑定了 AppComponentselectedHero.name 属性。 HeroDetailComponent 组件将会有一个 hero 属性,而不是 selectedHero 属性。 所以,我们要把模板中的所有 selectedHero 替换为 hero 。只改这些就够了。 最终结果如下所示:

template: '''
  <div *ngIf="hero != null">
    <h2>{{hero.name}} details!</h2>
    <div><label>id: </label>{{hero.id}}</div>
    <div>
      <label>name: </label>
      <input [(ngModel)]="hero.name" placeholder="name">
    </div>
  </div>'''

现在,我们的英雄详情布局只存在于 HeroDetailComponent 组件中了。

添加英雄属性

我们将刚刚所说的 hero 属性添加到组件类中。

Hero hero;

啊哦!我们定义的 hero 属性是 Hero 类型,但是我们的 Hero 类还在 app_component.dart 文件中。我们有了两个组件,它们都有各自的文件,并且都需要引用 Hero 类。

要解决这个问题,我们得从 app_component.dart 文件中把 Hero 类移到属于它自己的 hero.dart 文件中。

class Hero {
  final int id;
  String name;

  Hero(this.id, this.name);
}

然后在 app_component.darthero_detail_component.dart 的顶部附近添加下列 import 声明语句:

import 'hero.dart';

英雄是一个输入属性

HeroDetailComponent 必须被告知该显示哪个英雄。谁告诉它呢?自然是父组件 AppComponent 了!

AppComponent 确实知道该显示哪个英雄——用户从列表中选中的那个。而这个英雄就是 selectedHero 属性的值。

我们马上更新 AppComponent 的模板,以便把该组件的 selectedHero 属性绑定到 HeroDetailComponent 组件的 hero 属性上。绑定看起来可能是这样的:

<my-hero-detail [hero]="selectedHero"></my-hero-detail>

注意,在等号 = 左边方括号中的这个 hero 是属性绑定的 目标

Angular 要求我们必需把 目标属性 定义成组件的 输入属性。 否则, Angular 会拒绝绑定,并抛出一个错误。

我们有几种方式把 hero 声明成 输入属性 。 这里我们采用 首选 的方式:使用 @Input 注解 hero 属性。

@Input()
Hero hero;

刷新 AppComponent

我们回到 AppComponent,要教它如何使用 HeroDetailComponent 组件。

我们先导入 HeroDetailComponent 组件,以便可以引用它。

import 'hero_detail_component.dart';

在模板中,我们找到刚刚删除“英雄详情”的位置,添加表示 HeroDetailComponent 组件的元素标签:

<my-hero-detail></my-hero-detail>

在我们将 AppComponentselectedHero 属性绑定到 HeroDetailComponenthero 属性前,两个组件之间还不能协同工作,要像这样做:

<my-hero-detail [hero]="selectedHero"></my-hero-detail>

现在,AppComponent 的模板内容如下:

template: '''
  <h1>{{title}}</h1>
  <h2>My Heroes</h2>
  <ul class="heroes">
    <li *ngFor="let hero of heroes"
      [class.selected]="hero == selectedHero"
      (click)="onSelect(hero)">
      <span class="badge">{{hero.id}}</span> {{hero.name}}
    </li>
  </ul>
  <my-hero-detail [hero]="selectedHero"></my-hero-detail>
''',

多亏数据绑定机制,HeroDetailComponent 可以从 AppComponent 接收到英雄数据,并在列表下方显示那个英雄的详情。

现在还什么都没有发生!

我们点击列表内的英雄,并没有详细信息!我们在浏览器开发工具的控制台中找找看错误信息!但并没有出错。

似乎是Angular忽略了新的标签。确实如此!

指令列表

浏览器会忽略它无法识别的标签和特性(Attribute),Angular同样如此。

我们已经导入 HeroDetailComponent ,并在模板中使用它,但是我们并没有告诉Angular。

我们可以通过元数据的 directives 列表告诉 Angular 我们在模板中使用了哪些组件。让我们在配置对象 @Component 中, templatestyles 的后面,直接添加列表属性。

directives: const [HeroDetailComponent]

搞定!

当在浏览器中查看应用时,我们看到了英雄列表。当选中一个英雄时,还能看到所选英雄的详情。

从根本上的改变是,我们可以在应用中的任何地方使用这个 HeroDetailComponent 组件来显示英雄详情了。

我们创建了第一个可复用组件!

回顾应用结构

让我们来检查一下。在本章中,经过这些漂亮的重构,我们应该得到了下列结构:

我们已经走过的路

我们来盘点一下已经构建的内容:

  • 我们创建了一个可复用组件
  • 我们学会了如何让一个组件接收输入
  • 我们学会了把父组件绑定到子组件
  • 我们学会了在 directives 列表中声明我们需要的指令

前方的路

通过抽取共享组件,我们的《英雄之旅》变得更有复用性了。

但在 AppComponent 中,我们仍然使用着 mock 数据。显然,这种方式并不是可持续的。 我们应该将“数据访问”重构为一个独立的服务,并在需要数据的组件中共享它。

在 下一篇教程 中 ,我们将学习如何创建服务。

本文出自“Dart语言中文社区”,允许转载,转载时请务必以超链接形式标明文章原始出处
本文地址:
http://www.cndartlang.com/990.html

发表评论

登录后才能评论

评论列表(15条)

  • star
    star 2018年7月5日 上午10:40

    花了2天时间看完全部文档,写的好,node.js、angular、typescript都学过,感觉语言之间都大同小异,最近研究flutter,碰巧学习一下,好久没有更新了啊,您主要是做什么技术的啊

    • aa2
      aa2 2018年7月10日 下午9:34

      不错哦

      • aa2
        aa2 2018年7月10日 下午9:36

        不错哦

  • aa2
    aa2 2018年7月10日 下午9:54

    花了2天时间看完全部文档,写的好,node.js、angular、typescript都学过,感觉语言之间都大同小异,最近研究flutter,碰巧学习一下,好久没有更新了啊,您主要是做什么技术的啊

  • aa2
    aa2 2018年7月10日 下午10:05



    2













    node.js
    、angular
    、typescript
    都学过,
    感觉语言
    之间都大
    同小异,
    最近研究
    flutter,
    碰巧学习
    一下,好
    久没有更
    新了啊,
    您主要是
    做什么技
    术的啊

  • aa2
    aa2 2018年7月10日 下午10:07

    花了2天时间看完全部文档

  • aa2
    aa2 2018年7月10日 下午10:08

    花了2天时间看完全部文档,写

  • aa2
    aa2 2018年7月10日 下午10:08

    花了2天时间看完全部文档,写的好,

  • aa2
    aa2 2018年7月10日 下午10:09

    花了2天时间看完全部文档,写的好,node.js、

  • aa2
    aa2 2018年7月10日 下午10:10

    花了2天时间看完全部文档,写的好,node.js、angular、

  • aa2
    aa2 2018年7月10日 下午10:11

    花了2天时间看完全部文档,写的好,node.js、angular、typescript

  • aa2
    aa2 2018年7月10日 下午10:11

    花了2天时间看完全部文档,写的好,node.js、angular、typescript都学过,

  • aa2
    aa2 2018年7月10日 下午10:11

    花了2天时间看完全部文档,写的好,node.js、angular、typescript都学过,感

  • aa2
    aa2 2018年7月10日 下午10:12

    花了2天时间看完全部文档,写的好,node.js、angular、typescript都学过,感觉

  • aa2
    aa2 2018年7月10日 下午10:13

    花了2天时间看完全部文档,写的好,node.js、angular、typescript都学过,感觉语言