在Angular应用开发中,组件可以说是随处可见的。本篇文章将介绍几种常见的组件通讯场景,也就是让两个或多个组件之间交互的方法。
根据数据的传递方向,分为父组件向子组件传递、子组件向父组件传递及通过服务传递三种交互方法。
父组件向子组件传递
子组件通过@Input装饰器定义输入属性,然后父组件在引用子组件的时候通过这些输入属性向子组件传递数据,子组件可通过setter或ngOnChanges()来截听输入属性值的变化。
先定义两个组件,分别为子组件DemoChildComponent和父组件DemoParentComponent.
子组件:
@Component({ selector: 'demo-child', template: ` <p>{{paramOne}}</p> <p>{{paramTwo}}</p> ` }) export class DemoChildComponent { @Input() paramOne: any; // 输入属性1 @Input() paramTwo: any; // 输入属性2 }
子组件通过@Input()定义输入属性paramOne和paramTwo(属性值可以为任意数据类型)
父组件:
@Component({ selector: 'demo-parent', template: ` <demo-child [paramOne]='paramOneVal' [paramTwo]='paramTwoVal'></demo-child> ` }) export class DemoParentComponent { paramOneVal: any = '传递给paramOne的数据'; paramTwoVal: any = '传递给paramTwo的数据'; }
父组件在其模板中通过选择器demo-child引用子组件DemoChildComponent,并通过子组件的两个输入属性paramOne和paramTwo向子组件传递数据,最后在子组件的模板中就显示传递给paramOne的数据和传递给paramTwo的数据这两行文本。
通过 setter 截听输入属性值的变化
在实际应用中,我们往往需要在某个输入属性值发生变化的时候做相应的操作,那么此时我们需要用到输入属性的 setter 来截听输入属性值的变化。
我们将子组件DemoChildComponent进行如下改造:
@Component({ selector: 'demo-child', template: ` <p>{{paramOneVal}}</p> <p>{{paramTwo}}</p> ` }) export class DemoChildComponent { private paramOneVal: any; @Input() set paramOne (val: any) { // 输入属性1 this.paramOneVal = val; // dosomething }; get paramOne () { return this.paramOneVal; }; @Input() paramTwo: any; // 输入属性2 }
在上面的代码中,我们可以看到通过paramOne属性的 setter 将拦截到的值val赋值给内部私有属性paramOneVal,达到父组件传递数据给子组件的效果。当然,最重要的是,在 setter 里面你可以做更多的其它操作,程序的灵活性就更强了。
通过ngOnChanges()来截听输入属性值的变化
通过 setter 截听输入属性值的变化的方法只能对单个属性值变化进行监视,如果需要监视多个、交互式输入属性的时候,这种方法就显得力不从心了。而通过使用 OnChanges 生命周期钩子接口的 ngOnChanges() 方法(当组件通过@Input装饰器显式指定的那些变量的值变化时调用)就可以实现同时监视多个输入属性值的变化。
在子组件DemoChildComponent新增ngOnChanges:
@Component({ selector: 'demo-child', template: ` <p>{{paramOneVal}}</p> <p>{{paramTwo}}</p> ` }) export class DemoChildComponent implements OnChanges { private paramOneVal: any; @Input() set paramOne (val: any) { // 输入属性1 this.paramOneVal = val; // dosomething }; get paramOne () { return this.paramOneVal; }; @Input() paramTwo: any; // 输入属性2 ngOnChanges(changes: {[propKey: string]: SimpleChange}) { for (let propName in changes) { // 遍历changes let changedProp = changes[propName]; // propName是输入属性的变量名称 let to = JSON.stringify(changedProp.currentValue); // 获取输入属性当前值 if (changedProp.isFirstChange()) { // 判断输入属性是否首次变化 console.log(`Initial value of ${propName} set to ${to}`); } else { let from = JSON.stringify(changedProp.previousValue); // 获取输入属性先前值 console.log(`${propName} changed from ${from} to ${to}`); } } } }
新增的ngOnChanges方法接收的参数changes是以输入属性名称为键、值为SimpleChange的对象,SimpleChange对象含有当前输入属性是否第一次变化、先前值、当前值等属性。因此在ngOnChanges方法中通过遍历changes对象可监视多个输入属性值并进行相应的操作。
获取父组件实例
前面介绍的都是子组件通过@Input装饰器定义输入属性,这样父组件可通过输入属性将数据传递给子组件。
当然,我们可以想到一种更主动的方法,那就是获取到父组件实例,然后调用父组件的某个属性或方法来获取需要的数据。考虑到每个组件的实例都会添加到注入器的容器里,因此可通过依赖注入来找到父组件的示例。
子组件获取父组件实例相比于父组件获取子组件实例(直接通过模板变量、@ViewChild或@ViewChildren获取)要麻烦一些。
要在子组件中获取父组件的实例,有两种情况:
已知父组件的类型
这种情况可以直接通过在构造函数中注入DemoParentComponent来获取已知类型的父组件引用,代码示例如下: