引言:
上一篇我们初步了解的Angular中组件及基本的使用,但是所有的功能要是放到一个组件里面必然显得更加冗余,臃肿,我们通常会将组件按照一定的规则进行拆分,拆分后的组件免不了的就需要进行通信,这一篇我们就来一起熟悉一下Angular中的组件通信吧。
正文:
在实际的应用中我们的组件将会以树形的结构进行关联,所以组件间的关系主要就是:
- 父子关系
- 兄弟关系
- 无直接关系
准备一下我们的环境:
- 创建一个
header
组件:ng g c components/header
<app-button></app-button> <app-title></app-title> <app-button></app-button> 复制代码
export class HeaderComponent implements OnInit { constructor() {} ngOnInit(): void {} }
- 创建一个
title
组件:ng g c components/title
<span>{{title}}</span>
export class TitleComponent implements OnInit { public title: string = '标题'; constructor() {} ngOnInit(): void {} }
- 创建一个
button
组件:ng g c components/button
<button>{{ btnName }}</button>
export class ButtonComponent implements OnInit { public btnName: string = '按钮'; constructor() {} ngOnInit(): void {} }
直接调用
适用于父子关系组件,注意点是直接调用使得父子组件的耦合性变高,要明确使用确实需要直接调用。
- 将我们的header组件挂载到app中,使得app和header之间形成父子组件关系
- 使用
#
为我们的组件起一个名称:<app-header #header></app-header>
- 现在我们的header组件还很空,我们扩展一下,要不然调用什么呢?
export class HeaderComponent implements OnInit { public name: string = 'HeaderComponent'; printName(): void { console.log('component name is', this.name); } }
- 组件扩展好以后我们就可以在父组件app中调用子组件header中的属性和函数了
<app-header #header></app-header> <p> 调用子组件属性: {{ header.name }} <button (click)="header.printName()">调用子组件函数</button> </p>
- 第4步是在父组件的html模板中进行操作,有时候我们还需要在父组件的ts类中对子组件进行操作,我们接下来接着演示。
- 我们需要用到一个新的装饰器
@ViewChild(Component)
export class AppComponent { title = 'angular-course'; @ViewChild(HeaderComponent) private header!: HeaderComponent; // 声明周期钩子: 组件及子组件视图更新后调用,执行一次 ngAfterViewInit(): void { // 调用子组件属性 console.log(this.header.name); // 调用子组件函数 this.header.printName(); } }
@Input和@Output
适用于父子关系组件
- 我们通过在
header
组件中定义title
,来解耦title
组件中直接调用导致扩展复杂的问题 - 为
title
组件中的title
属性增加@Input()装饰器:@Input() public title: string = '标题';
- 为header组件新增title属性并赋值:
public title: string = '我是新标题';
- 我们再
header
组件的html
模板中这样来使用title
组件:<app-title [title]="title"></app-title>
- 一起看看到现在的效果吧,界面虽然丑,但是下次使用组件时
title
设置是不是方便一点呢?
- 以上步骤实现了父组件的数据传递到了子组件中,那么我们接着来看子组件的数据怎么传递到父组件中呢? 我们一起来用
@Output()
装饰器实现以下吧 - 在
title
组件的ts类中增加titleChange
属性:@Output() public titleChange = new EventEmitter();
- 在
title
组件的ts类中定时派发数据
ngOnInit(): void { // 定时将子组件的数据进行派发 setInterval(() => { this.titleChange.emit(this.title); }, 1500); }
- 现在我们来修改header父组件来接收派发来的数据:
<app-title [title]="title" (titleChange)="onChildTitleChange($event)"> </app-title>
onChildTitleChange(value: any) { console.log('onChildTitleChange: >>', value); }
利用服务单利进行通信
适用于无直接关系组件
- 既然要通过服务来做通信,那我们就先创建一个服务吧:
ng g s services/EventBus
,并且我们声明了一个类型为Subject
的属性来辅助通信
@Injectable({ providedIn: 'root', }) export class EventBusService { public eventBus: Subject<any> = new Subject(); constructor() {} }
- 我们为了省事就不重新创建组件了,因为我们的
header
中的按钮组件和title组件就符合没有直接关系的组件。 - 改造一下我们的
button
组件,并且添加点击事件来触发triggerEventBus
函数
export class ButtonComponent implements OnInit { public btnName: string = '按钮'; constructor(public eventBusService: EventBusService) {} ngOnInit(): void {} public triggerEventBus(): void { this.eventBusService.eventBus.next('我是按钮组件'); } }
- 在
title
组件中模拟数据的获取
export class TitleComponent implements OnInit { constructor(public eventBusService: EventBusService) {} ngOnInit(): void { this.eventBusService.eventBus.subscribe((value) => { console.log(value); }); } }
利用cookie、session或者localstorage进行通信
- 这个就很简单了,我们还是用
title
组件和button
组件来做演示,这次我们在title组件中将数据保存,在button
组件中获取数据。我们仅演示localstorage
吧,其他都雷同的。 - 在
title
组件的ngOnInit()
钩子中保存title
到localstorage
中:window.localStorage.setItem('title', this.title);
- 在button组件中获取数据:
const title = window.localStorage.getItem('title');
结语:
本篇我们介绍了Angular的组件通信,为我们拆分后的组件可以进行合理的通信提供了保障,我们到现在组件的使用都是通过引入标签的方式进行,那还有其他的方式来使用我们的组件吗?当然,下一篇我们将一起来熟悉Angular中的动态组件。