angular组件继承

Angular 2.3 版本中引入了组件继承的功能,该功能非常强大,能够大大增加我们组件的可复用性。

组件继承涉及以下的内容:
Metadata:如 @Input()、@Output()、@ContentChild/Children、@ViewChild/Children 等。在派生类中定义的元数据将覆盖继承链中的任何先前的元数据,否则将使用基类元数据。
Constructor:如果派生类未声明构造函数,它将使用基类的构造函数。这意味着在基类构造函数注入的所有服务,子组件都能访问到。
Lifecycle hooks:如果基类中包含生命周期钩子,如 ngOnInit、ngOnChanges 等。尽管在派生类没有定义相应的生命周期钩子,基类的生命周期钩子会被自动调用。
需要注意的是,模板是不能被继承的 ,因此共享的 DOM 结构或行为需要单独处理。了解详细信息,请查看 - properly support inheritance。
接下来我们来快速体验的组件继承的功能并验证以上的结论,具体示例如下(本文所有示例基于的 Angular 版本是 - 4.0.1):

exe-base.component.ts

import { Component, ElementRef, Input, HostBinding, HostListener, OnInit } from '@angular/core'; @Component({
    selector: 'exe-base', // template will not be inherited  template: `
    <div>
       exe-base:我是base组件么? - {{isBase}}
    </div>
  ` }) export class BaseComponent implements OnInit { @Input() isBase: boolean = true; @HostBinding('style.color') color = 'blue'; // will be inherited  @HostListener('click', ['$event']) // will be inherited  onClick(event: Event) { console.log(`I am BaseComponent`);
    } constructor(protected eleRef: ElementRef) { }

    ngOnInit() { console.dir('BaseComponent:ngOnInit method has been called');
    }
}

exe-inherited.component.ts

import { Component, HostListener, OnChanges, SimpleChanges } from '@angular/core'; import { BaseComponent } from './exe-base.component'; @Component({
    selector: 'exe-inherited',
    template: `
    <div>
      exe-inherited:我是base组件么? - {{isBase}}
    </div>
  ` }) export class InheritedComponent extends BaseComponent implements OnChanges { @HostListener('click', ['$event']) // overridden onClick(event: Event) { console.log(`I am InheritedComponent`);
    }
    ngOnChanges(changes: SimpleChanges) { console.dir(this.eleRef); // this.eleRef.nativeElement:exe-inherited }
}

app.component.ts

import { Component, OnInit } from '@angular/core'; import {ManagerService} from "./manager.service"; @Component({
  selector: 'exe-app',
  template: `
    <exe-base></exe-base>
    <hr/>
    <exe-inherited [isBase]="false"></exe-inherited>
  ` }) export class AppComponent {
  currentPage: number = 1;
  totalPage: number = 5;
}

接下来我们简要讨论一个可能令人困惑的主题,@Component() 中元数据是否允许继承?答案是否定的,子组件是不能继承父组件装饰器中元数据。限制元数据继承,从根本上说,是有道理的,因为我们的元数据用是来描述组件类的,不同的组件我们是需要不同的元数据,如 selector、template 等。Angular 2 组件继承主要还是逻辑层的复用,具体可以先阅读完下面实战的部分,再好好体会一下哈。

现在我们先来实现一个简单的分页组件,simple-pagination.component.ts

import { Component, Input, Output, EventEmitter } from '@angular/core'; @Component({
    selector: 'simple-pagination',
    template: `
       <button (click)="previousPage()" [disabled]="!hasPrevious()">Previous</button> 
       <button (click)="nextPage()" [disabled]="!hasNext()">Next</button>
       <p>page {{ page }} of {{ pageCount }} </p>
    ` }) export class SimplePaginationComponent { @Input() pageCount: number; @Input() page: number; @Output() pageChanged = new EventEmitter<number>();

    nextPage() { this.pageChanged.emit(++this.page);
    }

    previousPage() { this.pageChanged.emit(--this.page);
    }

    hasPrevious(): boolean { return this.page > 1;
    }

    hasNext(): boolean { return this.page < this.pageCount;
    }
}

app.component.ts

import { Component, OnInit } from '@angular/core'; import {ManagerService} from "./manager.service"; @Component({
  selector: 'exe-app',
  template: `
   <simple-pagination [page]="currentPage" [pageCount]="totalPage"></simple-pagination>
  ` }) export class AppComponent {
  currentPage: number = 2;
  totalPage: number = 10;
}

我们发现 UI 界面风格已经完全不一样了,但仔细想一下组件分页的控制逻辑仍可以继续使用。Angular 团队也考虑到了这种场景,因此为我们引入组件继承的特性,这对我们开发者来说,可以大大地提高组件的复用性。接下来我们来一步步实现新的分页组件,首先先更新 UI 界面,具体代码如下:
exe-pagination.component.ts

import { Component } from '@angular/core'; import { SimplePaginationComponent } from './simple-pagination.component'; @Component({
    selector: 'exe-pagination',
    template: `
    <a (click)="previousPage()" [class.disabled]="!hasPrevious()" 
      href="javascript:void(0)">
      ««
    </a> 
    <span>{{ page }} / {{ pageCount }}</span>
    <a (click)="nextPage()" [class.disabled]="!hasNext()"
      href="javascript:void(0)" >
      »»
    </a>
  ` }) export class ExePaginationComponent extends SimplePaginationComponent {
    
}

上面代码中,有几个注意点:
首先我们先导入已开发完的 SimplePaginationComponent 组件类
然后让我们新定义的 ExePaginationComponent 类继承于 SimplePaginationComponent 类
接着我们更新页面的视图模板,把按钮替换为 << 和 >>
我们看到更新的视图模板,我们仍然可以使用基类 (SimplePaginationComponent) 中定义的所有输入、输出属性
再继续开发 ExePaginationComponent 组件前,我们先来更新一下 SimplePaginationComponent 组件:

@Component({
  selector: 'simple-pagination',
  template: `
    <button (click)="previousPage()" [disabled]="!hasPrevious()">{{ previousText }}</button> 
    <button (click)="nextPage()" [disabled]="!hasNext()">{{ nextText }}</button>

    <p>page {{ page }} of {{ pageCount }}</p>
  ` }) export class SimplePaginationComponent {
  ... @Input()
  previousText = 'Previous'; @Input()
  nextText = 'Next';
  ...
}

注意:
当用户没有设置 previousText 输入属性值时,我们使用的默认值是 'Previous'
当用户没有设置 nextText 输入属性值时,我们使用的默认值是 'Next'
对于 ExePaginationComponent 组件,我们也希望让用户自定义 previousText 和 nextText 的值,但它们对应的默认值是:'<<' 和 '>>',这时我们可以覆盖 SimplePaginationComponent 组件的输入属性,具体示例如下:

import { Component , Input, Output} from '@angular/core'; import { SimplePaginationComponent } from './simple-pagination.component'; @Component({
    selector: 'exe-pagination',
    template: `
    <a (click)="previousPage()" [class.disabled]="!hasPrevious()" 
      href="javascript:void(0)">
      ««
    </a> 
    <span>{{ page }} / {{ pageCount }}</span>
    <a (click)="nextPage()" [class.disabled]="!hasNext()"
      href="javascript:void(0)" >
      »»
    </a>
  ` }) export class ExePaginationComponent extends SimplePaginationComponent { @Input() previousText = '<<'; // override default text @Input() nextText = '>>'; // override default text }

类的概念
虽然 JavaScript 中有类的概念,但是可能大多数 JavaScript 程序员并不是非常熟悉类,这里对类相关的概念做一个简单的介绍。

类 (Class):一种面向对象计算机编程语言的构造,是创建对象的蓝图,描述了所创建的对象共同的属性和方法。

对象 (Object):类的实例,通过 new 创建

面向对象 (OOP) 的三大特性:封装、继承、多态

封装 (Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据

继承 (Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还可以扩展自有的功能特性

多态 (Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自 Animal,但是分别实现了自己的 eat() 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat() 方法,程序会自动判断出来应该如何执行 eat()

存取器(getter & setter):用于属性的读取和赋值

修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法

抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现

接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,825评论 6 546
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,814评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,980评论 0 384
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 64,064评论 1 319
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,779评论 6 414
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,109评论 1 330
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,099评论 3 450
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,287评论 0 291
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,799评论 1 338
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,515评论 3 361
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,750评论 1 375
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,221评论 5 365
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,933评论 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,327评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,667评论 1 296
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,492评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,703评论 2 380

推荐阅读更多精彩内容

  • core package 概要:Core是所有其他包的基础包.它提供了大部分功能包括metadata,templa...
    LOVE小狼阅读 2,630评论 0 3
  • Angular 2架构总览 - 简书http://www.jianshu.com/p/aeb11061b82c A...
    葡萄喃喃呓语阅读 1,497评论 2 13
  • 1、我们一直想的,就会变成现实。 2、我们都在向外发射频率,悲观只会吸引到坏的东西。 3、仇视敌人,最终会导致自己...
    毛虫虫阅读 186评论 0 1
  • 沙漠里迷路许久的旅人 忘记了抽烟喝酒要烫头 全力着奔跑触碰那绿洲 眩晕在满盘砝码的赌局
    七指头陀李辉阅读 183评论 0 1
  • 当时楼月谁曾忆 ——记李煜 他形单只影,倚靠在栏杆上,只是怔怔地盯着手中的酒杯——呵,还是难逃一死么!他抬头,看到...
    _青宸_阅读 280评论 0 0