Angular Inter-component Communication
Contract: Following the official Angular statement, the AngularJS in the following refers to the version, and Angular refers to Angular2 and later upgraded versions.
When developing single-page applications (SPA) using Angular (or any MV*) front-end framework, we may encounter the following scenarios:
Component A and component B need to communicate with each other before, or the routing state of A needs to know the information about routing state of B and other business needs.
At this time, it is necessary to design a reasonable communication solution to the problems of data synchronization and data communication.
Data communication between AngularJS components
In AngularJS, that is, in the Angular JS version, we need to implement communication between controllers. There are many solutions, common ones include:
1. Use SharedService to use shared public services to realize data exchange.
Service in AngularJS is designed as a singleton, which provides underlying implementation feasibility for this solution, and this solution is also widely adopted.
2. Using the event mechanism provided by AngularJS, $rootScope.$broadcast/ $scope.$emit is implemented in conjunction with the $on method.
The implementation of this solution has certain limitations, such as: the $emit method can only pass events upwards, but cannot achieve downwards event propagation. But it is basically enough to make reasonable combinations.
3. Use the browser's SessionStorage or LocalStorage to exchange data.
Since these two types of local storage solutions provided by the browser provide corresponding storage events, we can also use this solution for data exchange. Use this solution to clean up irrelevant data in a timely manner.
4. Application of responsive programming ideas or observer mode. Regarding this type of implementation, it requires a transformation of programming ideas, which will be recorded through special articles.
5. Implement shared variable pools by itself. This is quite difficult and is not recommended without certain design capabilities.
Since AngularJS is not the focus of this article, I will only mention it briefly here. There are many common places for Angular's solution introduced later.
Data communication between Angular components
SharedService
Shared service solutions are still available in the new Angular without additional learning costs. There are records here in previous study notes, and no longer records.
SharedService with RxJS
I heard that SharedService and RxJS are more practical! The learning cost here lies in RxJS, which is just a JS implementation of Rx idea. It is highly recommended to learn Rx programming ideas here, and its learning income ratio is definitely beyond your imagination.
RxJS does not require us to manually go to SharedService to check whether the data has changed. Instead, when the data changes, we actively push the changed data to any interested subscriber.
Take a chestnut:
We have a piece of data that may change at any time. In the absence of RxJS (or similar framework/library), we want to know the changes in the data. We may use a polling mechanism to constantly ask Service A. We are concerned about whether the data has changed. If it changes, we will take corresponding actions. We are in a blind active state.
A more advanced implementation is that we may call the implementation of Service A an object that can be observed, so that we can obtain data changes by observing the status of Service A.
Now let's use RxJS implementation, which can now be understood as a more advanced observer pattern implementation. When RxJS is used, when the data in Service A changes, Service A will actively push the changed data to each interested 'consumer', who are in a state of passive acceptance of data and autonomously processing the data. What makes RxJS different from the ordinary observer mode is that it provides a series of data manipulation methods, such as: filter, map, etc. This will facilitate us to process data more carefully.
Here is a simple example code to experience it:
import { Injectable } from '@angular/core'; import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; @Injectable() export class DataService { private data: any; private subject: Subject<any> = new Subject<any>(); setData(data: any): void { = data; (data); } getData(): Observable<any> { return (); } }
In the above code, we simply implement an observable data service. Let's use this service below.
import { Component, OnInit } from '@angular/core'; import { DataService } from './shared/Dataservice'; @Component({ moduleId: , selector: '<my-component></my-component>', templateUrl: '.', providers: [DataService] }) export class MyComponent implements OnInit { constructor(private dataService: DataService) {} ngOnInit() { // Will invoke data handler everytime // when other component use the setter () ().subscribe(data => (data)); } }
Use the @Input and @Output decorators provided by Angular to implement communication services between components
The new Angular adopts the Web Component method to encapsulate components, and calls the organizational relationship between components a tree structure. This provides good support for the flow and management of application data, and also allows us to use some other libraries in Angular applications, such as: Redux idea. Based on these design concepts, Angular provides more powerful functions for instructions, and components are also instructions.
Use @Input to modify the attributes to realize communication between parent -> child components
The following sample code shows how to set the Input property of a component
import { Component, Input, OnInit } from '@angular/core'; @Component({ moduleId: , selector: 'child-component', template: `I'm {{ name }}` }) export class ChildComponent implements OnInit { @Input() name: string; constructor() { } ngOnInit() { } }
In the above code, we implement a component called ChildComponent, which has an attribute modified by @Input decorator: name.
Below we will show how to use this component and assign values to this Input property.
import { Component, OnInit } from '@angular/core'; import { ChildComponent } from './child-component'; @Component({ moduleId: , selector: 'parent-component', template: `<child-component [name]="childName"></child-component>`, // This is unnecessary when installing ChildComponent within Root NgModule directives: [ChildComponent] }) export class ParentComponent implements OnInit { private childName: string; constructor() { } ngOnInit() { = 'StevenShen'; } }
In the above code implementation, in the parent component, we set a specific value in the parent component for the Input property of the child component. The key point is in the following clip:
<child-component [name]="childName"></child-component>
When Angular performs AOT operations, it injects a specific value into the ChildComponent.
If you use CodePen or your own local experiment code, you will find that the properties modified by '@', '=', '&' are different from those in AngularJS instructions.
When the childName in the parent component changes, the name attribute in the ChildComponent does not perceive the change. What's wrong with this? Do you feel like the new version of Angular is kidding us, wtf! ! ! The inner expression is like this ○| ̄| . (I feel that the style of writing a study note has changed suddenly...)
Map the property changes of the parent component to the child component
The implementation of the previous section, although when initializing a child component, we can feed back the value of the parent component into the child component. However, after initialization is completed, changes in related attributes in the parent component cannot be perceived by the child component.
This undoubtedly makes us feel broken. Why is it different from AngularJS? ? ? Don't worry, we will provide solutions in the future.
Use the component lifecycle hook function ngOnChanges provided by Angular to listen for changes in input attribute values
It is necessary to enable the child components to perceive changes in the relevant attributes in the parent component. We need to have a certain understanding of the life cycle of Angular component, and use the hook function of the component life cycle provided by Angular to synchronize data between components. (About the life cycle of Angualr component, there will be relevant learning notes later. Add links to it.) Here is the code directly:
import { Component, Input, SimpleChanges } from '@angular/core'; @Component({ moduleId: , selector: 'child-component', template: `I'm {{ name }}` }) export class ChildComponent { @Input() name: string; ngOnChanges(changes: SimpleChanges) { = changes['childName'].currentValue; } }
Use getter and setter methods in ES5 to listen for input properties
In ES5, when we define the properties of an object, we can set the associated getter and setter methods for the properties of an object through methods. After we perform this operation, the corresponding getter/setter method will be called for preprocessing or changing the operation results.
By the same token, in Angular, we can intercept the changes in the relevant values in the parent component by setting and using a setter method of input properties and perform specific data processing.
This method is more intuitive, just enter the code:
Code implementation of parent component:
import { Component } from '@angular/core'; @Component({ moduleId: , selector: 'name-parent', template: ` <h2>Master controls {{}} names</h2> <name-child *ngFor="let name of names" [name]="name"></name-child> ` }) export class ParentComponent { names = ['StevenShen', ' ', ' lei ']; }
Code implementation of subcomponents
import { Component, Input } from '@angular/core'; @Component({ moduleId: , selector: 'name-child', template: `<h3>"{{name}}"</h3>` }) export class ChildComponent { name: string = 'default name'; @Input() set name(name: string) { = (name && ()) || 'default name'; } get name() { return ; } }
Use @Output to modify the attribute to realize communication between child -> parent components
In the new version of Angular, the communication between child components and parent components adopts an event mechanism. Such a design helps component reuse and code decoupling;
We don't need to care too much about the specific implementation of the component. We just need to know what data a component receives and what output events it generates.
Let’s directly upload the code to understand it intuitively:
@Component({ moduleId: , selector: 'child-component', template: `I'm {{ name }}` }) export class ChildComponent { @Input() name: string; @Output() say: EventEmitter<boolean> = new EventEmitter<boolean>(); ngOnChanges(changes: SimpleChange) { = changes['childName'].currentValue; } speak() { (true); } }
After the child component changes are completed, we will change the code implementation of the parent component.
import { Component, OnInit } from '@angular/core'; import { ChildComponent } from './child-component'; @Component({ moduleId: , selector: 'parent-component', template: `<child-component [name]="childName" (say)="isChildSpeak($event)"></child-component>`, // This is unnecessary when installing ChildComponent within Root NgModule directives: [ChildComponent] }) export class ParentComponent implements OnInit { private childName: string; constructor() { } ngOnInit() { = 'StevenShen'; } isChildSpeak(isIt: boolean) { ('The child speak status is %s', isIt ? 'ture' : 'false'); } }
This enables communication between parent-child components.
However, such implementations have certain limitations: the parent component cannot use data binding to read the properties of the child component or call the child component's methods.
Get the controller/template of the component to communicate between components through @ViewChild
Except using @Input and @Output decorators with Angular's lifecycle hook function for inter-component communication. We can also use @ViewChild to communicate between different components, not just limited to communication between parent and child components. At the same time, using @ViewChild method, we can obtain more granular component control permissions, such as reading the attribute value of the child component in the parent component or calling the child component method. We still use the above code to modify it.
For changes to ChildComponent component:
import { Component } from '@angular/core'; @Component({ moduleId: , selector: 'child-component', template: `I'm {{ name }}` }) export class ChildComponent { public name: string; speak() { ('say something whitout EventEmitter'); } }
Changes to ParentComponent component:
import { Component, OnInit, AfterViewInit, ViewChild, ElementRef } from '@angular/core'; import { ChildComponent } from './'; @Component({ moduleId: , selector: 'parent-component', // attention #childCmp tag template: ` <child-component #childCmp></child-component> <button (click)=" = childName"></button> `, // This is unnecessary when installing ChildComponent within Root NgModule directives: [ ChildComponent ] }) export class ParentComponent implements OnInit, AfterViewInit { @ViewChild('childCmp') childCmp: ElementRef; constructor() { } ngOnInit() { = 'StevenShen'; } ngAfterViewInit() { (); } }
Through the above code transformation, we can also realize communication between different components, and such component communication is no longer limited to communication between parent and child components.
Summarize
Due to technical level and time reasons, this article is completed relatively briefly. The main things I have sorted out are some solutions I actually use in my work.
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.