Angular组件学习小结

组件概念

组件是一部分封装好的功能。将功能封装好是为了进一步的复用,减少代码的重复度。

组件通常是按照template、style、script进行拆分。template就是模版,style是样式,script是控制器,负责业务处理。

组件的核心任务就是将数据渲染到视图,并监听用户在视图上的操作。

组件的创建

使用命令创建

使用angular cli创建,ng g c xxx(组件名)

手动创建

需要创建三个文件,html、css、ts。

ts 文件

ts文件通常包含component装饰器、class代码。

component 装饰器

component装饰器中一般有以下几个配置项:
(1) selector:选择器,将组件封装在选择器里,在外部html模版中就可以根据这个选择器来调用组件。

(2) template或templateUrl:如果是templateUrl,则对应的就是模板所在的位置;如果是template,对应的就是模版。模版可以用前后两个反引号( ​`​ )扩起来。

(3) styles或styleUrls: styles对应样式内容;styleUrls是样式文件路径。它们对应的都是一个字符串数组,也就是可以有多个样式文件。

class代码

class代码中通常包含两个部分:成员变量和方法。
成员变量用来保存状态或数据。
方法中可以编写用来响应用户操作的代码或者是生命周期的钩子函数。

组件的生命周期

当 Angular 实例化组件类并渲染组件视图及其子视图时,组件实例的生命周期就开始了。当 Angular 销毁组件实例并从 DOM 中移除它渲染的模板时,生命周期就结束了。

在Angular中,开发者可以在组件生命周期的某些节点调用生命周期钩子方法,从而能初始化组件、变更检测、组件实例被销毁之前做一些清理工作。

如果在组件中想实现生命周期的某个钩子方法,首先要让组件的class类实现对应的接口(如OnInit接口),然后才能在class中实现接口对应的方法(如OnInit接口对应ngOnInit()方法)。例如下面这段代码

export class PeekABooDirective implements OnInit {
  constructor(private logger: LoggerService) { }

  // implement OnInit‘s `ngOnInit` method
  ngOnInit() {
    this.logIt(’OnInit‘);
  }

  logIt(msg: string) {
    this.logger.log(`#${nextId++} ${msg}`);
  }
}

Angular组件的生命周期及其对应的钩子方法

按顺序如下:

ngOnChanges

当组件中绑定了输入属性,并且某个输入属性的值出现了变化,这个钩子方法就会被调用。

这个钩子方法接受一个SimpleChanges对象作为输入,SimpleChanges对象包含了输入属性的当前值和上一个值。

注意的是,如果输入属性是一个引用,当引用的对象的某个成员变量发生改变,但是引用指向没有发生变化时,ngOnChanges不会捕获到这个变化。

OnInit 、ngOnInit

在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后调用。只调用一次。

该钩子方法是用来初始化指令/组件。比如一些获取数据的操作不适合在组件构造器中进行。

ngDoCheck

第一次执行时,是在onInit之后执行。 紧跟在ngOnChanges后面执行

作用是可以检测到ngOnChanges无法检测到的变更

ngAfterContentInit

外部内容投影进组件视图或指令所在的视图之后调用。

ngAfterContentChecked()

检查完被投影到组件或指令中的内容之后调用。

ngAfterViewInit()

初始化完组件视图及其子视图或包含该指令的视图之后调用。

ngAfterViewChecked()

做完组件视图和子视图或包含该指令的视图的变更检测之后调用。

ngOnDestroy

每当 Angular 每次销毁指令/组件之前调用并清扫。 在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏。

钩子函数的调用频率

通过了解钩子函数的调用频率,可以将对性能影响较大的代码尽可能减少在调用频率高的钩子函数中

组件的通信

这里主要讲父组件与子组件之间的通信。通信,可以理解为信息传递,就是将组件中的数据或发生的事件传递给另一个组件。

从父组件到子组件的通信

@Input

@Input可以作为子组件属性的装饰器,它可以将属性声明成输入属性。父组件可以在模板文件中将数据通过输入属性传递给子组件。

下面这两段代码就是关于利用输入属性的例子。

// 在子组件中
// selector:app-hero-child
export class HeroChildComponent {
  @Input() hero!: Hero;
  @Input(’master‘) masterName = ’‘;
}
// 在父组件模版中

<app-hero-child
      *ngFor=”let hero of heroes“
      [hero]=”hero“
      [master]=”master“>
</app-hero-child>
// 父组件class
export class HeroParentComponent {
  heroes = HEROES;
  master = ’Master‘;
}
setter

如果你不仅想从输入属性中获取到父组件的数据,还想在数据发生变化时作出反应,那么使用一个输入属性的 setter,将可以拦截父组件中值的变化,并采取行动。

setter是一个方法。setter的参数,可以作为输入属性。在setter方法中,可以先对父组件传递进来的数据进行处理,之后再赋值给子组件中的属性。

具体使用看代码

// 子组件中
// selector: ’app-name-child‘
export class NameChildComponent {
  @Input()
  get name(): string { return this._name; }
  set name(name: string) {
    this._name = (name && name.trim()) ||<no name set>;
  }
  private _name = ’‘;
  //注意_name是不对外暴露的
}


 <h2>Master controls {{names.length}} names</h2>

    <app-name-child *ngFor=”let name of names“ [name]=”name“></app-name-child>

ngOnChanges

ngChanges也能拦截输入属性的变化,但是相比于setter,ngChanges可以同时截获多个输入属性的变化。因此,如果需要监视多个输入属性时,使用ngChanges也许更加方便。

以下是代码片段展示


import { Component, Input, OnChanges, SimpleChanges } from ’@angular/core‘;

@Component({
  selector: ’app-version-child‘,
  template: `
    <h3>Version {{major}}.{{minor}}</h3>
    <h4>Change log:</h4>
    <ul>
      <li *ngFor=”let change of changeLog“>{{change}}</li>
    </ul>
  `
})
export class VersionChildComponent implements OnChanges {
  @Input() major = 0;
  @Input() minor = 0;
  changeLog: string[] = [];

  ngOnChanges(changes: SimpleChanges) {
    const log: string[] = [];
    for (const propName in changes) {
      const changedProp = changes[propName];
      const to = JSON.stringify(changedProp.currentValue);
      if (changedProp.isFirstChange()) {
        log.push(`Initial value of ${propName} set to ${to}`);
      } else {
        const from = JSON.stringify(changedProp.previousValue);
        log.push(`${propName} changed from ${from} to ${to}`);
      }
    }
    this.changeLog.push(log.join(,));
  }
}

从子组件到父组件的通信

@Output装饰器

@output装饰器通常放在输出属性前,并且输出属性一般是EvnEmitter类型的,EvnEmitter类型利用了设计模式—发布订阅模式,当子组件中发生事件时,可以调用emit方法发布该事件,事件的信息可以作为emit的参数。在父组件模版代码中,可以向输出属性注册处理函数,当子组件的事件发布时,这个函数就会被调用。

下面是一般方法中使用EventEmitter的三个步骤:创建、注册/订阅、发布

let em = new EventEmitter();
// 注册处理函数
em.subscribe(func);//func是处理函数

// 发布事件
em.emit(params);//params是参数

以下代码是Output装饰器的使用,可以看到emit调用在子组件中,而在父组件模板中注册了处理函数,处理函数在父组件中有定义。

// 子组件中
// selector: ’app-voter‘,
export class VoterComponent {
  @Input()  name = ’‘;
  @Output() voted = new EventEmitter<boolean>();
  didVote = false;

  vote(agreed: boolean) {
    this.voted.emit(agreed);
    this.didVote = true;
  }
}

// 父组件中
<app-voter
      *ngFor=”let voter of voters“
      [name]=”voter“
      (voted)=”onVoted($event)“>
    </app-voter>

// 父组件中 class
export class VoteTakerComponent {
  agreed = 0;
  disagreed = 0;
  voters = [’Narco‘, ’Celeritas‘, ’Bombasto‘];

  onVoted(agreed: boolean) {
    if (agreed) {
      this.agreed++;
    } else {
      this.disagreed++;
    }
  }
}

本地变量

如果你想读取子组件的属性或调用子组件的方法,你可以在父组件模板中创建一个本地变量代表子组件,然后在模板中利用这个变量来读取子组件的属性和调用子组件的方法。

使用方法如下:


// 父组件模板中
<button (click)=”timer.start()“>Start</button>
<app-countdown-timer #timer></app-countdown-timer>

ViewChild()

本地变量,只能在模板中使用,如果想在父组件的类中访问子组件的属性和方法,可以通过ViewChild装饰器。ViewChild可以将子组件注入到父组件中。

ViewChild 是属性装饰器,用来从模板视图中获取匹配的元素。视图查询在 ngAfterViewInit 钩子函数调用前完成,因此在 ngAfterViewInit 钩子函数中,才能正确获取查询的元素。

同一个服务

将同一个服务注入到父组件与子组件中,服务中利用发布订阅模式进行信息传递。父组件或子组件中的数据或事件首先发布到服务中,服务再调用注册的处理函数。

。。。。未完待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值