为了账号安全,请及时绑定邮箱和手机立即绑定

Angular2官网项目

标签:
Html/CSS

接着昨天的来

    给我们的英雄们“美容”

    重构.app/app.component.scss

.container{

h1{

text-align: center;

color: red;

}

.row{

line-height: 30px;

padding: 10px 0;

label{

text-align: right;

}

}

.controller{

padding:0 360px;

li{

height: 50px;

}

label{

@extend %link;

text-align: center;

background: #A4D3EE;


}

span{

@extend %link;

text-indent: 5px;

background: #c5f2f2;

}

li:hover{

label{

background: #dce2e0;

}

span{

background: #cfeccf;

}

}

}

}

%link{

height: 40px;

line-height: 40px;

}

一: 选择英雄

我们的应用已经有了英雄列表和单个英雄的详情视图。 但列表和单独的英雄之间还没有任何关联。 我们希望用户在列表中选中一个英雄,然后让这个被选中的英雄出现在详情视图中。 这种 UI 布局模式,通常被称为“主从结构”。 在这个例子中,主视图是英雄列表,从视图则是被选中的英雄。

接下来,我们要通过组件中的一个selectedHero属性来连接主从视图,它被绑定到了点击事件上。

        

        处理点击事件

                我们再往<li>元素上插入一句点击事件的绑定代码:

                <li *ngFor="let item of generals" (click)="oSelect(item)">

圆括号标识<li>元素上的click事件是绑定的目标。 等号右边的onSelect(item)表达式调用 AppComponent onSelect()方法,并把模板输入变量item作为参数传进去。 它是我们前面在ngFor指令中定义的那个item变量。

详细绑定参考https://angular.cn/guide/template-syntax#event-binding

    添加点击处理器以暴露选中的英雄

我们不再需要AppComponentgeneral属性,因为不需要再显示单个的英雄,我们只需要显示英雄列表。但是用户可以点选一个英雄。 所以我们要把hero属性替换成 selectgeneral 属性。

 ./app/app.component.ts

    添加selectGeneral:General;

在用户选取一个英雄之前,所有的英雄名字都应该是未选中的。所以我们不希望像General一样初始化 selectgeneral 变量。

现在,添加一个onSelect方法,用于将用户点击的英雄赋给selectgeneral 属性。

./app/app.component.ts

oSelect(item:General):void{

  this.selectGeneral=item;

  }

.app/app.component.html

    添加

    

<div class="hero_view" >

<div class="row" >

<label class="col-lg-5">id: </label>`selectGeneral`.`id` `selectGeneral`.`name` `selectGeneral`.`source`

</div>

<div class="row" >

<label class="col-lg-5">name: </label>

<input class="col-lg-2"  [(ngModel)]='selectGeneral.name' placeholder="name">

</div>

<div class="row" >

<label class="col-lg-5">source: </label>

<input class="col-lg-2" [(ngModel)]='selectGeneral.source' placeholder="name">

</div>

</div>

使用 ngIf 隐藏空的详情

当应用加载时,我们会看到一个英雄列表,但还没有任何英雄被选中。 selectedHero属性是undefined。 因此,我们会看到浏览器控制台中出现下列错误:

AppComponent.html:6 ERROR TypeError: Cannot read property 'name' of undefined

    我们可以把模板中的英雄详情内容区放在一个<div>中。 然后,添加一个ngIf内置指令,把ngIf的值设置为组件的selectedHero属性。


    <div class="hero_view" *ngIf="selectGeneral">-----------》别忘了ngIf前的星号 (*)。


当没有选中英雄时,ngIf指令会从 DOM 中移除表示英雄详情的这段 HTML 。 没有了表示英雄详情的元素,也就不用担心绑定问题。

当用户选取了一个英雄,selectedHero变成了“已定义的”值,于是ngIf把英雄详情加回 DOM 中,并计算它所嵌套的各种绑定。

想了解更多*ngIF-->https://angular.cn/guide/template-syntax#ngif-指令

给选中英雄添加样式

我们在下面的详情区看到了选中的英雄,但是我们还是没法在上面的列表区快速定位这位英雄。

在我们前面添加的styles元数据中,有一个名叫selected的自定义CSS类。 要想让选中的英雄更加醒目,当用户点击一个英雄名字时,我们要为<li>添加selected类。 例如,当用户点击“Magneta”时,它应该使用不一样的醒目的背景色。

<li>上添加一个[class.selected]绑定:


<li *ngFor="let item of generals" (click)="oSelect(item)" [class.selected]="item==selectGeneral">

当表达式(item === selectGeneral)为true时,Angular会添加一个CSS类selected。为false时则会移除selected类。

重构样式

./app/app.component.scss

.container{

h1{

text-align: center;

color: red;

}

.row{

line-height: 30px;

padding: 10px 0;

label{

text-align: right;

}

}

.controller{

padding:0 360px;

li{

height: 50px;

margin-bottom:5px;

}

label{

@extend %link;

text-align: center;

background: #A4D3EE;


}

span{

@extend %link;

text-indent: 5px;

}

li:hover{

background: #c5f2f2;

}

}

}

%link{

height: 40px;

line-height: 40px;

}

.hero_view{

.row{

padding:10px 0;

label{

text-align:right;

}

input{

text-indent: 10px;

}

}

}

.selected{

color:white;

background: #babfc1;

}

二:多个组件

AppComponent负责所有事。 起初,它只显示单个英雄的详情。然后,它变成了主从结构,同时显示英雄列表和一个英雄详情。 现在,我们很快又会有新需求了。 我们不能把这些需求全都放在一个组件中,否则将不可维护。

    我们要把它拆分成一些子组件,每个子组件只聚焦在一个特定的任务或工作流上。 最后,AppComponent将会变成一个简单的壳,用来作为那些子组件的宿主。

    a.制作英雄详情组件

    在./app文件下新建general文件夹

        .app/general/general-detail.component.ts  这个文件中会存放这个新的 GeneralDetailComponent。

    

    文件名和组件名遵循风格指南中的标准方式。

  • 组件的类名应该是大驼峰形式,并且以Component结尾。 因此英雄详情组件的类名是GeneralDetailComponent

  • 组件的文件名应该是小写中线形式,每个单词之间用中线分隔,并且以.component.ts结尾。 因此GeneralDetailComponent类应该放在general-detail.component.ts文件中。


    GeneralDetailComponent的代码如下:

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

    

    @Component({

      selector: 'gener-detail',

    })

    export class GeneralDetailComponent {

    }

要定义一个组件,我们总是要先导入符号Component

@Component装饰器为组件提供了Angular元数据。 CSS选择器的名字hero-detail会匹配元素的标签名,用于在父组件的模板中标记出当前组件的位置。 最后,我们会把<hero-detail>添加到AppComponent的模板中。

总是export这个组件类,因为你必然会在别处import它。

英雄详情的模板

    

要把英雄详情的视图移入GeneralDetailComponent,只要把英雄详情的 内容 从AppComponent模板的底部剪切出来, 粘贴到@Component元数据的template属性中就可以了。

GeneralDetailComponent有一个 general属性,而不再是selectGeneral。 所以我们也要在模板中把所有的selectGeneral替换为general。 这些完成之后,新的模板是这样的

    @Component({

      selector: 'gener-detail',

      template:`

    <div class="hero_view" *ngIf="general">

    <div class="row" >

    <label class="col-lg-5">id: </label>`general`.`id` `general`.`name` `general`.`source`

    </div>

    <div class="row" >

    <label class="col-lg-5">name: </label>

    <input class="col-lg-2"  [(ngModel)]='general.name' placeholder="name">

    </div>

    <div class="row" >

    <label class="col-lg-5">source: </label>

    <input class="col-lg-2" [(ngModel)]='general.source' placeholder="source">

    </div>

    </div>

      `,

    })

    

添加general属性

GeneralDetailComponent模板绑定到了该组件的genreal属性上。 把这个属性添加到GeneralDetailComponent类上,就像这样:

        general:General;

因为使用了General类所以需要引入

  

        import {General}  from "../../bean/General";

general属性是一个输入属性

    .app/app.component.html

            <general-detail [general]='selectGeneral'></general-detail>

在等号的左边,是方括号围绕的general属性,这表示它是属性绑定表达式的目标。 我们要绑定到的目标属性必须是一个输入属性,否则Angular会拒绝绑定,并抛出一个错误。   

        首先,修改@angular/core导入语句,使其包含符号Input

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

            然后,通过在eneral属性前面加上@Input装饰器,来表明它是一个输入属性。

                      @Input()general:General;

现在,general属性是GeneralDetailComponent类中唯一的东西。


它所做的一切就是通过它的输入属性general接收一个英雄对象,然后把这个属性绑定到自己的模板中。

下面是完整的GeneralDetailComponent

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

    

    import {General}  from "../../bean/General";

    @Component({

      selector: 'general-detail',

      template:`

    <div class="hero_view" *ngIf="general">

    <div class="row" >

    <label class="col-lg-5">id: </label>`general`.`id` `general`.`name` `general`.`source`

    </div>

    <div class="row" >

    <label class="col-lg-5">name: </label>

    <input class="col-lg-2"  [(ngModel)]='general.name' placeholder="name">

    </div>

    <div class="row" >

    <label class="col-lg-5">source: </label>

    <input class="col-lg-2" [(ngModel)]='general.source' placeholder="source">

    </div>

    </div>

      `,

      styleUrls:["../app.component.scss"]

    })

    export class GeneralDetailComponent {

      @Input()general:General;

    }

重要:在AppModule中声明GeneralDetailComponent

每个组件都必须在一个(且只有一个)Angular模块中声明。

,app/app.module.ts

import {GeneralDetailComponent} from './general/general-detail.component';

     declarations: [

        AppComponent,

        GeneralDetailComponent

      ],

完整的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 {GeneralDetailComponent} from './general/general-detail.component';


@NgModule({

  declarations: [

    AppComponent,

    GeneralDetailComponent

  ],

  imports: [

    BrowserModule,

    FormsModule

  ],

  providers: [],

  bootstrap: [AppComponent]

})

export class AppModule { }

有哪些变化?

仍然像以前一样,一旦用户点击了英雄的名字,英雄详情就会显示在英雄列表的下方。 不过现在改用HeroDetailView来表示英雄详情了。

我们把原来的AppComponent重构成了两个组件具有一些显著优点,无论是现在还是未来:

  1. 通过缩减AppComponent的职责,我们简化了它。

  2. 我们将来可以把GeneralDetailComponent改进为功能更丰富的英雄编辑器,而不用动AppComponent

  3. 同样,我们也可以改进AppComponent而不用动英雄详情视图。

  4. 我们可以在未来的其它父组件的模板中复用GeneralDetailComponent



    

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消