目录
  • 引言
  • 零、知识铺垫
    • CSS选择器
  • 一、什么是父子组件
    • 二、父组件调用子组件的方法
      • 三、父组件向子组件传值
        • 子组件使用@input装饰器接收数据
        • 父组件使用方括号[]发送数据
        • 升级:子组件通过set方法监听传入数据变化
        • 另一种升级:子组件通过ngOnChanges()生命周期钩子监听传入数据变化
      • 四、子组件向父组件传值
        • 子组件向父组件弹射事件
        • 父组件监听子组件弹射的事件
      • 五、总结
        • 六、后记
          • 总结

            引言

            对于稍微接触过Angular组件的同学来说,组件间交互应该没有什么问题。

            本文想追求的是用一个通俗解释,帮助自己理解的更准确。

            零、知识铺垫

            CSS选择器

            在介绍父子组件之前,先要了解一个概念——selector、选择器

            我们定义一个新组件时,一定会有这个属性:

            @Component({
              selector: 'app-village-edit', ①
              templateUrl: './village-edit.component.html',
              styleUrls: ['./village-edit.component.scss']
            })

            其中①就是选择器,就是告诉别的组件,如果想调用我这个组件,就要使用本组件的选择器<selectorName></selectorName>来调用。

            本质上就是定义了组件的HTML标签,就像常见的<p>标签、<button>标签一样。

            一、什么是父子组件

            就像现实中父母和孩子的关系是相对的一样,一个人对于它的父母来说,就承担了孩子的角色;对于它的孩子来说则承担了父母的角色。

            父组件和子组件也是相对的。

            假设,一个组件在自己的HTML模板中,通过选择器(也就是特定的HTML标签)来调用其他组件时。我们称这个组件为父组件,而那个被调用的组件称为子组件

            二、父组件调用子组件的方法

            定义了两个类:

            child.component.ts,它的选择器selector是: 'app-child'

            parent.component.ts,它的选择器selector是: 'app-parent',

            此时,在parent组件的HTMl中引用child组件的选择器:

            <app-child></app-child>

            这样就完成了子组件的调用。
            此时,如果通过路由加载父组件,就会发现子组件也会在特定的位置被渲染出来。

            三、父组件向子组件传值

            子组件使用@input装饰器接收数据

            子组件从父组件接收的值,会保存到子组件的变量中。

            所以用来接收传值的变量与普通变量唯一的区别,就是在常规的变量上增加一个@input()注解。

            定义普通变量是这样的:

            master = 'Master';

            如果用来接收传值,只要改成这样:

            @Input() master = 'Master';

            这样,master变量默认是'Master'字符串。

            但如果父组件向其传值,变量就变成了接收的值。

            父组件使用方括号[]发送数据

            常规方式调用子组件:

            <app-child></app-child>

            如果子组件可以接收数据,就可以用[propertyName] = value的方法来传值。
            例如:

            <app-child [master]="hero"> </app-child>

            用这种写法可以实现:一旦组件渲染完成后,子组件中的master变量就是'hero'的值了。

            当父组件中的变量值变化时,子组件也会同步变化,也就是说,子组件可以监听传过来的值的变化信息

            升级:子组件通过set方法监听传入数据变化

            在上面的方式中,对于传过来的值,虽然可以监听变化,但局限在于:子组件只能直接使用传入的值。

            如果想对传入的值进行处理或过滤,就要稍微调整一下子组件。

            常规情况下,子组件是通过给变量加上@Input装饰器来接收参数的:

            @Input() name = 'name';

            如果想处理参数,只需要把接收传值的变量变成set方法即可:

            @Input() 
            get name(): string { return this._name; }
            set name(name: string) { 
                // 此处可以增加其他处理逻辑
                this._name = name; 
            } 
            private _name = '';

            此时,_name是内部变量,当父组件对于name属性传入值的时候,会自动执行set name方法给_name赋值并增加其他的处理逻辑。

            另一种升级:子组件通过ngOnChanges()生命周期钩子监听传入数据变化

            官方文档中写到:“当需要监视多个、交互式输入属性的时候,ngOnChanges()比用属性 setter 方法更合适。”

            常规情况下,子组件是通过给变量加上@Input装饰器来接收参数的:

            @Input() param1 = 'string1';
            @Input() param2 = 'string2';

            当我们要监听多个变量的变化并做出反应时,可以用ngOnChanges()方法:

            @Input() param1 = 'string1';
            @Input() param2 = 'string2';
            
            ngOnChanges(changes: SimpleChanges) {  ①
            
                for (const propName in changes) {  ②
                
                    // 通过变量名获取变化信息
                    const changedProp = changes[propName];  
                    // 获取上一个值
                    const from = JSON.stringify(changedProp.previousValue); ③
                    // 获取当前值
                    const to = JSON.stringify(changedProp.currentValue);     ④
                    // 此处可以添加其他处理过程了 ⑤
                 } 
                 
            }

            ① 执行ngOnChanges()方法时,可以用一个SimpleChanges参数来获得当前组件所有参数的变化情况。

            ② 通过循环获得每一个参数的上一个值和当前值。

            ③ 获得上一个值④ 获得当前值⑤ 根据业务逻辑添加其他处理过程

            注:由于ngOnChanges方法调用非常频繁,会导致性能问题或者软件崩溃,所以建议少用。

            四、子组件向父组件传值

            子组件向父组件弹射事件

            刚刚讲到了子组件如何获取父组件的传入的变量,如何监听父组件的变化,以及如何处理传入的值。

            接下来讲反向的传输:父组件如何监听子组件的变化,并做出反应。

            定义普通变量是这样的:

            param1 = 'String1';

            如果想把这个变量暴露给父组件,需要在变量前加入@output()装饰器,并且给他赋值一个变量弹射器

            @Output() param1 = new EventEmitter<string>();

            此处EventEmitter是变量弹射器,EventEmitter需要一个确定的类型

            但此时,这个param1变量就不能再用等号"="赋值了,如果想让父组件监听到变化,就需要用弹射方法.emit

            this.param1.emit("String2");

            接下来前往父组件。

            父组件监听子组件弹射的事件

            刚刚已经在子组件设置好了暴露的变量,那么父组件如何接收呢?

            常规的父组件调用子组件:

            <app-child></app-child>

            如果想监听子组件的某个变量,可以使用圆括号():

            <app-child (param1)="function1($event)">
            
            </app-child>

            $event 是Angular内置的事件变量。

            function1我们在父组件中定义的处理变化的方法。

            使用方式如下:

            function1(param2: boolean) { 
                // 这个param2为我们自己定义的参数名,
                // 本质上是子组件中变化的param1参数,但不用和子组件中的参数名相同
                // 在此处增加处理过程即可
            }

            此时,当param1的值发生变化,就会执行function1,并且传入一个事件,事件的实质内容就是子组件定义的param1参数。

            function1方法把参数作为param2接收,并添加处理过程。

            五、总结

            • Angular中,在HTML通过selector选择器调用的组件称为子组件
            • 父组件向子组件传值使用方括号[]
            • 子组件有两种方式接收值: @input + 变量名、@Input + set方法
            • 子组件想父组件传递事件使用EventEmitter
            • 父组件接收事件使用圆括号(),并声明一个处理方法用来调用

            熟悉的风格,一图胜千言:

            如何通过简单的代码描述Angular父组件、子组件传值

            六、后记

            是不是有似曾相识的感觉,在刚开始接触Angular时就知道,可以使用方括号[]来绑定原生HTML标签的某些属性,例如:

            <p [id]="sayHelloId" [style.color]="fontColor">
                You can set my color in the component!
            </p>

            另一方面,还有一个相似之处就是,Angular中也是使用圆括号()来绑定原生HTML标签的某个方法,例如:

            <button (click)="onClick()"> 
                点我!
            </button>

            这些是巧合吗?并不是。

            我们可以这样理解:

            Angular中所有的原生HTML标签都变成了组件

            之所以很多标签中可以用方括号[]绑定属性、使用圆括号()绑定方法,是因为Angular已经为我们扩展了原生的HTML标签,使它们具备了接收和发送数据的能力!

            换言之,在Angular内部的组件中,已经为我们加上了许许多多的@input@output装饰器,我们才能方便的绑定这些属性和方法。

            总结

            声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。