zoukankan      html  css  js  c++  java
  • 我们把主从结构的页面重构成多个组件

    多个组件

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

    首先老规矩,我们得让我们的代码运行起来:

    让应用代码保持转译和运行

    我们要启动 TypeScript 编译器,它会监视文件变更,并启动开发服务器。只要敲:

    npm start

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

    制作英雄详情组件

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

    我们当前的组件违反了单一职责原则。 虽然这只是一个教程,但我们还是得坚持做正确的事 — 况且,做正确的事这么容易,在此过程中,我们又能学习如何构建 Angular 应用。

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

    拆分英雄详情组件

    app目录下添加一个名叫hero-detail.component.ts的文件,并且创建HeroDetailComponent。代码如下:

    import {Component,Input } from '@angular/core';
    @Component({
    
     selector:"my-hero-detail",
    
    })
    export class HeroDetailComponent{
    
    }
    View Code

    命名约定

    我们希望一眼就能看出哪些类是组件,哪些文件包含组件。

    你会注意到,在名叫app.component.ts的文件中有一个AppComponent组件,在名叫hero-detail.component.ts的文件中有一个HeroDetailComponent组件。

    我们的所有组件名都以Component结尾。所有组件的文件名都以.component结尾。

    这里我们使用小写中线命名法 (也叫烤串命名法)拼写文件名, 所以不用担心它在服务器或者版本控制系统中出现大小写问题。

    我们先从 Angular 中导入ComponentInput装饰器,因为马上就会用到它们。

    我们使用@Component装饰器创建元数据。在元数据中,我们指定选择器的名字,用以标识此组件的元素。 然后,我们导出这个类,以便其它组件可以使用它

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

    英雄详情模板

    此时,AppComponent的英雄列表和英雄详情视图被组合进同一个模板中。 让我们从AppComponent剪切出英雄详情的内容,并且粘贴HeroDetailComponent组件的template属性中。

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

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

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

    添加 HERO 属性

    把刚刚所说的hero属性添加到组件类。

    hero: Hero;

    我们声明hero属性是Hero类型,但是我们的Hero类还在app.component.ts文件中。 我们有了两个组件,它们位于各自的文件,并且都需要引用Hero类。

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

    app/hero.ts

    export class Hero {
      id: number;
      name: string;
    }
    View Code

    我们从hero.ts中导出Hero类,因为我们要从两个组件文件中引用它。 在app.component.tshero-detail.component.ts的顶部添加下列 import 语句:

    import {Hero} from  './hero';

    HERO属性是一个输入属性

    还得告诉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;

    更新 AppModule

    回到应用的根模块AppModule,让它使用HeroDetailComponent组件。

    我们先导入HeroDetailComponent组件,后面好引用它。

    import { HeroDetailComponent } from './hero-detail.component';

    接下来,添加HeroDetailComponentNgModule装饰器中的declarations数组。 这个数组包含了所有由我们创建的并属于应用模块的组件、管道和指令。

    @NgModule({
      imports: [
        BrowserModule,
        FormsModule
      ],
      declarations: [
        AppComponent,
        HeroDetailComponent
      ],
      bootstrap: [ AppComponent ]
    })
    export class AppModule { }
    View Code

    更新 AppComponent

    现在,应用知道了我们的HeroDetailComponent, 找到我们刚刚从模板中移除英雄详情的地方, 放上用来表示HeroDetailComponent组件的元素标签。

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

    这两个组件目前还不能协同工作,直到我们把AppComponent组件的selectedHero 属性和HeroDetailComponent组件的hero属性绑定在一起,就像这样:

    <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>
    `,
    View Code

    感谢数据绑定机制,HeroDetailComponent应该能接收来自AppComponent的英雄数据,并在列表下方显示英雄的详情。 每当用户选中一个新的英雄时,详情信息应该随之更新。

    搞定!

    当在浏览器中查看应用时,可以看到英雄列表。 当选中一个英雄时,可以看到所选英雄的详情。

    值得关注的进步是:我们可以在应用中的任何地方使用这个HeroDetailComponent组件来显示英雄详情。

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

    完整的代码文件如下

    app/hero-detail.component.ts

    import { Component, Input } from '@angular/core';
    import { Hero } from './hero';
    @Component({
      selector: 'my-hero-detail',
      template: `
        <div *ngIf="hero">
          <h2>{{hero.name}} details!</h2>
          <div><label>id: </label>{{hero.id}}</div>
          <div>
            <label>name: </label>
            <input [(ngModel)]="hero.name" placeholder="name"/>
          </div>
        </div>
      `
    })
    export class HeroDetailComponent {
      @Input()
      hero: Hero;
    }
    View Code

    app/app.component.ts

    import { Component } from '@angular/core';
    import { Hero } from './hero';
    const HEROES: Hero[] = [
      { id: 11, name: 'Mr. Nice' },
      { id: 12, name: 'Narco' },
      { id: 13, name: 'Bombasto' },
      { id: 14, name: 'Celeritas' },
      { id: 15, name: 'Magneta' },
      { id: 16, name: 'RubberMan' },
      { id: 17, name: 'Dynama' },
      { id: 18, name: 'Dr IQ' },
      { id: 19, name: 'Magma' },
      { id: 20, name: 'Tornado' }
    ];
    @Component({
      selector: 'my-app',
      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>
      `,
      styles: [`
        .selected {
          background-color: #CFD8DC !important;
          color: white;
        }
        .heroes {
          margin: 0 0 2em 0;
          list-style-type: none;
          padding: 0;
           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.selected:hover {
          background-color: #BBD8DC !important;
          color: white;
        }
        .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;
        }
      `]
    })
    export class AppComponent {
      title = 'Tour of Heroes';
      heroes = HEROES;
      selectedHero: Hero;
      onSelect(hero: Hero): void {
        this.selectedHero = hero;
      }
    }
    View Code

    app/hero.ts

    export class Hero {
      id: number;
      name: string;
    }
    View Code

    app/app.module.ts

    import { NgModule }      from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { FormsModule }   from '@angular/forms';
    import { AppComponent }  from './app.component';
    import { HeroDetailComponent } from './hero-detail.component';
    @NgModule({
      imports: [
        BrowserModule,
        FormsModule
      ],
      declarations: [
        AppComponent,
        HeroDetailComponent
      ],
      bootstrap: [ AppComponent ]
    })
    export class AppModule { }
    View Code

     

    走过的路

    来盘点一下我们已经构建了什么。

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

    • 我们学会了如何让一个组件接收输入

    • 我们学会了在 Angular 模块中声明该应用所需的指令。 只要把这些指令列在NgModule装饰器的declarations数组中就可以了。

    • 我们学会了把父组件绑定到子组件。

     

    前方的路

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

    AppComponent中,我们仍然使用着模拟数据。 显然,这种方式不能“可持续发展”。 我们要把数据访问逻辑抽取到一个独立的服务中,并在需要数据的组件之间共享。

    在下一步,我们将学习如何创建服务。 

  • 相关阅读:
    gradle build scan 插件使用
    gradle 配置java 项目maven 依赖
    gradle 插件
    gradle java 简单项目使用
    gradle 命令行
    gradle wrapper 简单使用
    gradle 安装试用
    linux 使用asciinema 进行命令行屏幕录制共享
    ansible安装基本使用
    ansible playbook 使用
  • 原文地址:https://www.cnblogs.com/Playfunny/p/6148718.html
Copyright © 2011-2022 走看看