第二章 组件

点此处可以下载本章完成后的 示例代码 。运行方法为:

  1. 解压缩文件,进入heroes项目根目录。
  2. 使用npm install补上依赖包。也可以把之前备份的node_modules目录复制一份在项目根目录下。
  3. 使用npm start运行项目,打开浏览器访问http://localhost:4200/进入项目。

组件负责控制屏幕上的某一块区域,我们也称这块区域为视图,即组件是用来控制视图显示的逻辑。我们在类中定义组件的应用逻辑,为视图提供支持。组件通过一些由属性和方法组成的API与视图交互。

1. 编写第一个组件

下面我们开始新建第一个组件,这个组件的作用是在页面中显示一个Hero的信息。

命令行中进入该项目目录,使用命令 ng g c heroes/hero-list 新建组件文件。

这里的 ng g c heroes/hero-list 是工具 angular/cli的命令,使用了简写,不使用简写时的语句是这样 ng generate component heroes/hero-list,参数generate是说明要生成文件,参数 component是说明要生成一个组件,heroes/hero-list指的是要在src/app目录下建一个heroes/hero-list目录,我们要生成组件的名字就是hero-list。更多用法可以查看说明

我们可以看到输出:

ng g c heroes/hero-list
installing component
  create src/app/heroes/hero-list/hero-list.component.css
  create src/app/heroes/hero-list/hero-list.component.html
  create src/app/heroes/hero-list/hero-list.component.spec.ts
  create src/app/heroes/hero-list/hero-list.component.ts
  update src/app/app.module.ts

说明angular/cli工具已经帮我们在app下新建一个heroes/hero-list文件夹,4个文件。注意ng命令同时更新了app.module.ts这个文件,如果想删除这个组件,除了删除对应的目录外,还应该删除app.module.ts中相关的内容。

|---hero-list.component.css           是CSS样式文件
|---hero-list.component.html          是html文件
|---hero-list.component.spec.ts       是测试文件,我们开发过程中几乎用不到
|---hero-list.component.ts            是我们需要的hero组件文件

打开hero-list.component.ts文件:

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-hero-list',
  templateUrl: './hero-list.component.html',
  styleUrls: ['./hero-list.component.css']
})
export class HeroListComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}

这些都是angular/cli工具自动填写的。

  • selector属性是CSS选择器的名字, app-hero-list 会匹配元素的标签名,用于在父组>件中的模板中标记出当前组件的位置。(在父组件中使用标签<app-hero-list></app-hero-list> 即可调用heroComponent组件。
  • templateUrl属性是外联HTML文件
  • stylesUrls属性是外联样式CSS文件。

组件的命名

  • 组件的类名应该是大驼峰形式 ,并且以 Component 结尾。例如一个显示英雄列表的组件名称为HeroListComponent
  • 组件的文件名应该是小写中线形式,每个单词使用中线分隔,并且以.component.ts 结尾。

组件引入的包

组件的文件中需要引入名叫 Component 包。

例如,HeroListComponent组件中肯定需要包含这样一行代码:

import { Component } from '@angular/core';

请注意:要定义一个组件,总是要先导入符号 Component。

显示英雄名字
  1. 打开hero-list.component.ts,添加变量hero

     export class HeroListComponent implements OnInit {
    
       constructor() { }
    
       hero = 'The Monkey King';
    
       ngOnInit() {
       }
    
     }
    
  2. 打开hero-list.component.html,修改内容如下。

文件:hero-list.component.html

<h1>Hello {{hero}}!</h1>

这里的双大括号是Angular中的插值表达式绑定语法。它们表示的组件中的hero属性的值会作为字符串插入到HTML中间。

这个文件中的代码称为模板,我们通过组件的自带的模板定义组件视图。模板以html形式存在,告诉Angular如何渲染组件。多数情况下,模板看起来很像标准的HTML,但是也有不同的地方。

  1. 打开app.component.html。在文件内容替换为:
<app-hero-list></app-hero-list>
  1. 打开浏览器,可以看到:

    Hello The Monkey King!

目前为止,一个显示Hello的组件已经做好了。

下面我们来改进这个组件。

2. 英雄对象

显然,一个英雄有许多属性,所以我们需要把hero从一个字符串变量换成一个类。 创建一个Hero类,它具有idnamedesc属性。分别表示一个英雄的id,姓名,相关描述。 现在,把下列代码放到hero-list.component.ts的顶部,仅次于import语句。

export class Hero {
    id: number;
    name: string;
    desc: string;
}

一般我们总是 export 组件类,因为我们肯定会在别的地方import它。

现在,有了一个Hero类,我们把组件中类型为stringhero属性换成类型为Herohero属性。然后以1为 id ,以The Monkey King为 name ,初始化它。

hero: Hero = {
    id: 1,
    name: 'The Monkey King',
    desc: 'I can fly!'
}

同时也要更新模板中的绑定表达式,来引用hero的两个属性。

文件:hero-list.component.html

<h1>Hello {{hero.name}}!</h1>
<h2>Your id is: {{hero.id}}</h2>

打开浏览器,我们可以看到修改后的结果了。

Hello The Monkey King!
Your id is 1

3. 编辑英雄

用户应该能在一个<input>输入框中编辑英雄的名字。当用户输入时,这个输入框应该能同时显示和修改英雄的<name>属性 也就是说,我们要在表单元素<input>和组件<hero.name>属性之间建立双向绑定。

双向绑定

打开hero-list.component.html文件,修改内容为:

<h1>Hello {{hero.name}}!</h1>
<h2>Your id is {{hero.id}}</h2>
<div>
    <label>name:</label>
    <input [(ngModel)]="hero.name" placeholder="name">
</div>
<div>
    <label>id:</label>
    <input [(ngModel)]="hero.id" placeholder="id">
</div>

[(ngModel)] 是一个Angular语法,用与把hero.name绑定到输入框中,它的数据流是双向的:从属性到输入框,从输入框回到属性。 要使用[(ngModel)],我们必须导入FormsModule模块。

导入 FormsModule

打开app.module.ts文件,从@angular/forms导入符号FormsModule。然后把FormsModule添加到@NgModule元数据的imports数组中,它是当前应用正在使用的外部模块列表。

修改后的app.module.ts:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { HeroListComponent } from './heroes/hero-list/hero-list.component';

@NgModule({
  declarations: [
    AppComponent,
    HeroListComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

关于FormsModulengModel的更多知识,可以查看表单模板语法

我们可以修改hero的名字和id,可以看到这个改动立刻体现在上方的内容中。

现在我们已经新建了一个组件,来显示一个英雄的信息。 实际情况是我们需要管理多个英雄,所以下面我们来扩展这个实例,让它显示英雄列表。

4. 英雄列表

调整组件结构

Angular官方指导建议一个ts文件中只有一个类,所以我们将之前的组件中的类放到新的文件中。

 将Hero类放到新文件中
  • 使用命令ng g class domain/hero新建Hero类的文件。
  • hero-list.component.ts中的Hero类的内容剪切到新建的文件中。
  • hero-list.component.ts中添加import { Hero } from '../../domain/hero'

在这里domain文件夹用来存放对象类文件

现在,hero-list.component.ts文件是:

import { Component, OnInit } from '@angular/core';

import { Hero } from '../../domain/hero';

@Component({
  selector: 'app-hero-list',
  templateUrl: './hero-list.component.html',
  styleUrls: ['./hero-list.component.css']
})
export class HeroListComponent implements OnInit {

  constructor() { }

  hero: Hero = {
    id: 1,
    name: 'The Monkey King',
    desc: 'I can fly!'
  };

  ngOnInit() {
  }
}

hero.ts文件是:

export class Hero {
    id: number;
    name: string;
    desc: string;
}
模拟数据

要想显示更多的hero信息,我们就需要获取一些hero数据,当然在实际情况中是从web服务器中获取数据,但现在我们用一个数组模拟web api。在后续的章节中,我们后介绍到web api的使用。

在app目录下新建一个文件mock-heroes.ts,在这个文件中我们添加一个hero数组:

import { Hero } from './domain/hero';

export const HEROES: Hero[] = [
    { id: 1, name: 'Mr.Nice' , desc: 'I am Mr.Nive. I like apple.'},
    { id: 2, name: 'Narco' , desc: 'I am Narco. I like banana.'},
    { id: 3, name: 'Bombasto' , desc: 'I am Bombasto. I like orange.'},
    { id: 4, name: 'Celeritas', desc: 'I am Celeritas. I like grape.'},
    { id: 5, name: 'Magneta', desc: 'I am Magneta. I like strawberry.'},
    { id: 6, name: 'Rubbermale', desc: 'I am Rubbermale. I like pears.'},
    { id: 7, name: 'Dynama', desc: 'I am Dynama. I like tomato.'},
    { id: 8, name: 'Dr IQ', desc: 'I am Dr IQ. I like peach.' },
    { id: 9, name: 'Magma', desc: 'I am Magma. I like sandwich.'},
    { id: 10, name: 'Tornado', desc: 'I am Tornado. I like hamburger.'}
];

别忘了import Hero,因为hero类在另一个hero.ts中。

显示英雄列表

hero-list.component.ts文件中,添加一个公共属性,用来暴露这些hero,以供绑定。

import { Component, OnInit } from '@angular/core';

import { Hero } from '../../domain/hero';
import { HEROES } from '../../mock-heroes';

@Component({
  selector: 'app-hero-list',
  templateUrl: './hero-list.component.html',
  styleUrls: ['./hero-list.component.css']
})

export class HeroListComponent implements OnInit {

  constructor() { }

  heroes = HEROES;

  ngOnInit() {
  }

}

在这里,我们没有明确定义heroes属性的数据类型,是因为Typescript能从HEROES推断出来。

改变视图模板

我们在模板中用一个无序列表来显示英雄的名字。 打开hero-list.component.html文件,添加以下代码:

<h2>All heroes</h2>
<ul class="heroes">
  <li *ngFor="let hero of heroes">
    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>

这里,我们需要做的就是将模拟的数据填充到这个模板中去。

使用ngFor来显示英雄列表

我们想要把HEROES数组绑定到模板中,迭代并逐个显示它们。所以,我们使用了内置指令*ngFor

ngFor的*前缀表示

  • 及其子元素组成了一个主控模板。 ngFor指令在HeroComponent.heroes属性返回的heroes数组上迭代,并输出此模板的实例。 引号中赋值给ngFor的那段文本表示“从heroes数组中取出每个英雄,存入一个局部的hero变量,并让它在相应的模板实例中可用”。 要了解更多关于ngFor和模板输入变量的知识,查看用ngFor显示数组属性模板语法章ngFor
  • 这时候,我们打开浏览器可以看到,有了英雄的列表。

    英雄列表

    添加样式

    英雄们是有了,但是,样子实在的太简陋了。我们需要为英雄们加上样式,这就要修改样式单文件。打开 hero.componet.css ,并添加如下的样式:

    .heroes {
      margin: 0 0 2em 0;
      list-style-type: none;
      padding: 0;
      width: 15em;
    }
    .heroes li {
      cursor: pointer;
      position: relative;
      left: 0;
      background-color: #EEE;
      margin: .5em;
      padding: .3em 0;
      height: 1.6em;
      border-radius: 4px;
    }
    .heroes li:hover {
      color: #607D8B;
      background-color: #DDD;
      left: .1em;
    }
    .heroes .text {
      position: relative;
      top: -3px;
    }
    .heroes .badge {
      display: inline-block;
      font-size: small;
      color: white;
      padding: 0.8em 0.7em 0 0.7em;
      background-color: #607D8B;
      line-height: 1em;
      position: relative;
      left: -1px;
      top: -4px;
      height: 1.8em;
      margin-right: .8em;
      border-radius: 4px 0 0 4px;
    }
    

    打开浏览器,我们可以看到英雄列表的样式发生了变化。 带样式的英雄列表

    小结

    到此为止,你已经可以显示或修改一个英雄的信息,也可以显示一个英雄列表,但是如果想查看英雄的详细的信息或者修改列表中的英雄信息,我们需要更多的组件,这样代码会更清晰。

    results matching ""

      No results matching ""