您的当前位置:首页正文

angular 组件通信的几种实现方式

2020-11-27 来源:爱go旅游网

单页面应用组件通信有以下几种,这篇文章主要讲 Angular 通信

  • 父组件 => 子组件
  • 子组件 => 父组件
  • 组件A = > 组件B
  • 父组件 => 子组件 子组件 => 父组件 sibling => sibling
    @input @output
    setters (本质上还是@input) 注入父组件
    ngOnChanges() (不推荐使用)
    局部变量
    @ViewChild()
    service service service
    Rxjs的Observalbe Rxjs的Observalbe Rxjs的Observalbe
    localStorage,sessionStorage localStorage,sessionStorage localStorage,sessionStorage

    上面图表总结了能用到通信方案,期中最后3种,是通用的,angular的组件之间都可以使用这3种,其中Rxjs是最最牛逼的用法,甩redux,promise,这些同样基于函数式的状态管理几条街,下面一一说来

    父组件 => 子组件

    @input,最常用的一种方式

    @Component({
     selector: 'app-parent',
    template: '<div>childText:<app-child [textContent] = "varString"></app-child></div>',
     styleUrls: ['./parent.component.css']
    })
    export class ParentComponent implements OnInit {
     varString: string;
     constructor() { }
     ngOnInit() {
     this.varString = '从父组件传过来的' ;
     }
    }
    import { Component, OnInit, Input } from '@angular/core';
    @Component({
     selector: 'app-child',
     template: '<h1>{{textContent}}</h1>',
     styleUrls: ['./child.component.css']
    })
    export class ChildComponent implements OnInit {
     @Input() public textContent: string ;
     constructor() { }
     ngOnInit() {
     }
    }
    

    setter

    setter 是拦截@input 属性,因为我们在组件通信的时候,常常需要对输入的属性处理下,就需要setter了,setter和getter常配套使用,稍微修改下上面的child.component.ts

    child.component.ts

    import { Component, OnInit, Input } from '@angular/core';
    @Component({
     selector: 'app-child',
     template: '<h1>{{textContent}}</h1>',
     styleUrls: ['./child.component.css']
    })
    export class ChildComponent implements OnInit {
    _textContent:string;
     @Input()
     set textContent(text: string){
     this._textContent = !text: "啥都没有给我" ? text ;
     } ;
     get textContent(){
     return this._textContent;
     }
     constructor() { }
     ngOnInit() {
     }
    }

    onChange

    这个是通过angular生命周期钩子来检测,不推荐使用,要使用的话可以参angular文档

    @ViewChild()

    @ViewChild() 一般用在调用子组件非私有的方法

     import {Component, OnInit, ViewChild} from '@angular/core';
     import {ViewChildChildComponent} from "../view-child-child/view-child-child.component";
     @Component({
     selector: 'app-parent',
     templateUrl: './parent.component.html',
     styleUrls: ['./parent.component.css']
     })
     export class ParentComponent implements OnInit {
     varString: string;
     @ViewChild(ViewChildChildComponent)
     viewChildChildComponent: ViewChildChildComponent;
     constructor() { }
     ngOnInit() {
     this.varString = '从父组件传过来的' ;
     }
     clickEvent(clickEvent: any) {
     console.log(clickEvent);
     this.viewChildChildComponent.myName(clickEvent.value);
     }
     }
    
     import { Component, OnInit } from '@angular/core';
     @Component({
     selector: 'app-view-child-child',
     templateUrl: './view-child-child.component.html',
     styleUrls: ['./view-child-child.component.css']
     })
     export class ViewChildChildComponent implements OnInit {
     constructor() { }
     name: string;
     myName(name: string) {
     console.log(name);
     this.name = name ;
     }
     ngOnInit() {
     }
     }
    

    局部变量

    局部变量和viewChild类似,只能用在html模板里,修改parent.component.html,通过#viewChild这个变量来表示子组件,就能调用子组件的方法了.

    <div class="panel-body">
     <input class="form-control" type="text" #viewChildInputName >
     <button class=" btn btn-primary" (click)="viewChild.myName(viewChildInputName.value)">局部变量传值</button>
     <app-view-child-child #viewChild></app-view-child-child>
     </div>

    child 组件如下

    @Component({
     selector: 'app-view-child-child',
     templateUrl: './view-child-child.component.html',
     styleUrls: ['./view-child-child.component.css']
    })
    export class ViewChildChildComponent implements OnInit {
    
     constructor() { }
     name: string;
     myName(name: string) {
     console.log(name);
     this.name = name ;
     }
     ngOnInit() {
     }
    
    }
    
    

    子组件 => 父组件

    @output()

    output这种常见的通信,本质是给子组件传入一个function,在子组件里执行完某些方法后,再执行传入的这个回调function,将值传给父组件

    parent.component.ts

    @Component({
     selector: 'app-child-to-parent',
     templateUrl: './parent.component.html',
     styleUrls: ['./parent.component.css']
    })
    export class ChildToParentComponent implements OnInit {
    
     childName: string;
     childNameForInject: string;
     constructor( ) { }
     ngOnInit() {
     }
     showChildName(name: string) {
     this.childName = name;
     }
    }
    
    

    parent.component.html

    <div class="panel-body">
     <p>output方式 childText:{{childName}}</p>
     <br>
     <app-output-child (childNameEventEmitter)="showChildName($event)"></app-output-child>
    </div>
     child.component.ts
     export class OutputChildComponent implements OnInit {
     // 传入的回调事件
     @Output() public childNameEventEmitter: EventEmitter<any> = new EventEmitter();
     constructor() { }
     ngOnInit() {
     }
     showMyName(value) {
     //这里就执行,父组件传入的函数
     this.childNameEventEmitter.emit(value);
     }
    }
    

    注入父组件

    这个原理的原因是父,子组件本质生命周期是一样的

    export class OutputChildComponent implements OnInit {
     // 注入父组件
     constructor(private childToParentComponent: ChildToParentComponent) { }
     ngOnInit() {
     }
     showMyName(value) {
     this.childToParentComponent.childNameForInject = value;
     }
    }
    

    sibling组件 => sibling组件

    service

    Rxjs

    通过service通信

    angular中service是单例的,所以三种通信类型都可以通过service,很多前端对单例理解的不是很清楚,本质就是
    ,你在某个module中注入service,所有这个modul的component都可以拿到这个service的属性,方法,是共享的,所以常在app.moudule.ts注入日志service,http拦截service,在子module注入的service,只能这个子module能共享,在component注入的service,就只能子的component的能拿到service,下面以注入到app.module.ts,的service来演示

    user.service.ts

    @Injectable()
    export class UserService {
     age: number;
     userName: string;
     constructor() { }
    }
    

    app.module.ts

    @NgModule({
     declarations: [
     AppComponent,
     SiblingAComponent,
     SiblingBComponent
     ],
     imports: [
     BrowserModule
     ],
     providers: [UserService],
     bootstrap: [AppComponent]
    })
    export class AppModule { }
    SiblingBComponent.ts
    @Component({
     selector: 'app-sibling-b',
     templateUrl: './sibling-b.component.html',
     styleUrls: ['./sibling-b.component.css']
    })
    export class SiblingBComponent implements OnInit {
     constructor(private userService: UserService) {
     this.userService.userName = "王二";
     }
     ngOnInit() {
     }
    }
    

    SiblingAComponent.ts

    @Component({
     selector: 'app-sibling-a',
     templateUrl: './sibling-a.component.html',
     styleUrls: ['./sibling-a.component.css']
    })
    export class SiblingAComponent implements OnInit {
     userName: string;
     constructor(private userService: UserService) {
     }
     ngOnInit() {
     this.userName = this.userService.userName;
     }
    }

    通过Rx.js通信

    这个是最牛逼的,基于订阅发布的这种流文件处理,一旦订阅,发布的源头发生改变,订阅者就能拿到这个变化;这样说不是很好理解,简单解释就是,b.js,c.js,d.js订阅了a.js里某个值变化,b.js,c.js,d.js立马获取到这个变化的,但是a.js并没有主动调用b.js,c.js,d.js这些里面的方法,举个简单的例子,每个页面在处理ajax请求的时候,都有一弹出的提示信息,一般我会在
    组件的template中中放一个提示框的组件,这样很繁琐每个组件都要来一次,如果基于Rx.js,就可以在app.component.ts中放这个提示组件,然后app.component.ts订阅公共的service,就比较省事了,代码如下

    首先搞一个alset.service.ts

    import {Injectable} from "@angular/core";
    import {Subject} from "rxjs/Subject";
    @Injectable()
    export class AlertService {
     private messageSu = new Subject<string>(); //
     messageObserve = this.messageSu.asObservable();
     private setMessage(message: string) {
     this.messageSu.next(message);
     }
     public success(message: string, callback?: Function) {
     this.setMessage(message);
     callback();
     }
    }
    

    sibling-a.component.ts

    @Component({
     selector: 'app-sibling-a',
     templateUrl: './sibling-a.component.html',
     styleUrls: ['./sibling-a.component.css']
    })
    export class SiblingAComponent implements OnInit {
     userName: string;
     constructor(private userService: UserService, private alertService: AlertService) {
     }
     ngOnInit() {
     this.userName = this.userService.userName;
     // 改变alertService的信息源
     this.alertService.success("初始化成功");
     }
    }
    

    app.component.ts

    @Component({
     selector: 'app-root',
     templateUrl: './app.component.html',
     styleUrls: ['./app.component.css']
    })
    export class AppComponent {
     title = 'app';
     message: string;
     constructor(private alertService: AlertService) {
     //订阅alertServcie的message服务
     this.alertService.messageObserve.subscribe((res: any) => {
     this.message = res;
     });
     }
    }
    

    这样订阅者就能动态的跟着发布源变化

    总结:

    显示全文