Explain the communication between Angualr components in detail

  • 2021-07-13 04:02:27
  • OfStack

Communication between Angualr components

Convention: Following the official statement of Angular, the AngularJS generation below refers to version 1. x, and the Angular generation refers to Angular2 and later upgraded versions.

When developing a single-page application (SPA) using the front-end framework of Angular (or any MV*), we may encounter the following scenarios:

A components and B components need to communicate with each other before, or A routing state needs to know B routing state information and other business requirements.

At this time, it is necessary to design a reasonable communication scheme to solve the problems of data synchronization and data communication.

Data Communication Between AngularJS Components

In AngularJS, that is, Angular JS 1.x, we need to realize the communication between controllers. There are many schemes, and the common ones are:

1. SharedService is adopted, and shared public services are used to realize data exchange.

Service in AngularJS is designed as a singleton, which provides the low-level implementation feasibility for this 1 scheme, which is also widely used.

2. Using the event mechanism provided by AngularJS, $rootScope. $broadcast/$scope. $emit is implemented with $on method.

The implementation of this scheme has certain limitations, for example, the $emit method can only pass events upwards, but cannot propagate events downwards. However, it is basically enough to carry out reasonable collocation and combination.

3. Use the browser's SessionStorage or LocalStorage for data exchange.

Since both types of local storage schemes provided by browsers provide corresponding storage events, we can also use this scheme for data exchange. When using this scheme, attention should be paid to cleaning up irrelevant data in time.

4. Adopt responsive programming idea or the application of observer mode. With regard to this class 1 implementation, it needs to undergo a change in programming thinking, which will be recorded through a special article.

5. Implement shared variable pool by yourself. This is more difficult, and it is not recommended to have a certain design ability.

Since AngularJS is not the focus of this article, it is only mentioned briefly here. The scheme of Angular introduced later also has many common points.

Data Communication Between Angular Components

SharedService

The Shared Services scenario is still available in the new Angular and does not require additional learning costs. It is recorded here in the previous study notes, but it is no longer recorded.

SharedService with RxJS

I heard that SharedService and RxJS are more practical! The learning cost here lies in RxJS, and RxJS is only the JS implementation of Rx idea. It is strongly recommended to learn Rx programming ideas here. Her learning income ratio is absolutely beyond your imagination.

RxJS does not need us to check whether the data has changed manually in SharedService, but actively pushes the changed data to any interested subscribers when the data has changed.

Take chestnuts:

We have a data that may change at any time in the service A. Without using RxJS (or similar framework/library), we want to know the data changes. We may use polling mechanism to constantly ask the service A whether the data we care about has changed, and if so, make corresponding actions. We are in a state of blind initiative.

A more advanced point of implementation is that we might refer to the service A implementation as an observable object, so that we can obtain data changes by observing the state of the service A.

Now let's use the RxJS implementation, which can now be understood as a more advanced observer mode implementation. When the data in the service A changes after using the incoming RxJS, the service A will actively push the changed data to every interested'consumer ', who is in a state of passively accepting data and processing data independently. RxJS differs from ordinary observer mode in that it provides a series of data manipulation methods, such as filter, map and so on. This makes it easier for us to process data more finely.

Here's a simple sample code to experience 1:


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 {
 this.data = data;
 this.subject.next(data);
 }

 getData(): Observable<any> {
 return this.subject.asObservable();
 }
}

In the above code, we simply implemented an observable data service. Let's use this service.


import { Component, OnInit } from '@angular/core';

import { DataService } from './shared/Dataservice';

@Component({
 moduleId: module.id,
 selector: '<my-component></my-component>',
 templateUrl: '.name.component.html',
 providers: [DataService]
})
export class MyComponent implements OnInit {

 constructor(private dataService: DataService) {}

 ngOnInit() {
 // Will invoke data handler everytime
 // when other component use the setter this.dataService.setData()
 this.dataService.getData().subscribe(data => console.log(data));
 }
}

Use the @ Input and @ Output decorators provided at the bottom of Angular to implement communication services between components

The new Angular uses Web Component to encapsulate components, and designs the organizational relationship between components as a tree structure. This provides good support for the flow and management of application data, and also enables us to use some other libraries in Angular applications, such as Redux ideas. Based on these design concepts, Angular provides more powerful functions for instructions, and components are also instructions.

Use @ Input to modify the attribute to realize parent- > Communication between child Components

The following sample code shows how to set the Input property of a 1 component


import { Component, Input, OnInit } from '@angular/core';

@Component({
 moduleId: module.id,
 selector: 'child-component',
 template: `I'm {{ name }}`
})
export class ChildComponent implements OnInit {

 @Input() name: string;

 constructor() { }

 ngOnInit() { }
}

In the above code, we implemented a component called ChildComponent, which has a property decorated with the @ Input decorator: name.

Let's show you how to use this component and assign a value to this Input property.


import { Component, OnInit } from '@angular/core';
import { ChildComponent } from './child-component';

@Component({
 moduleId: module.id,
 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() { 
 this.childName = 'StevenShen';
 }
}

In the above code implementation, in the parent component, we set the specific value in the parent component for the Input property of the child component. The key points are in the following fragments:


<child-component [name]="childName"></child-component>

The Angular injects a specific value into the ChildComponent during the AOT operation.

If you test the above code on CodePen, or your own local code, you will find that the instructions of AngularJS use '@', '=', ' & 'Where the attributes of modification are different.

When the childName in the parent component changes, the name attribute in the ChildComponent does not sense the change. What's the matter? Do you feel that the new version of Angular is joking with us, wtf! ! ! The inner expression is this 0  ̄ _. (I feel that the style of writing a study note has changed suddenly...)

Map property changes of parent components to child components

The implementation of section 1 above, although we can feed back the value of the parent component to the child component when initializing the child component. However, after initialization, the changes of related attributes in the parent component cannot be perceived by the child component.

This undoubtedly makes our hearts collapse. Why is it different from AngularJS? ? ? Don't worry, we will provide solutions in the future.

The component lifecycle hook function ngOnChanges provided by Angular is used to monitor the change of input attribute value

We need to make the sub-components perceive the changes of related attributes in the parent components. We need to have a definite understanding of the life cycle of Angualr components, and use the hook function of the component life cycle provided by Angular to synchronize the data among components. (With regard to the lifecycle of Angualr components, there will be related study notes later. Add links when the time comes.) The code is directly on here:


import { Component, Input, SimpleChanges } from '@angular/core';

@Component({
 moduleId: module.id,
 selector: 'child-component',
 template: `I'm {{ name }}`
})
export class ChildComponent {

 @Input() name: string;

 ngOnChanges(changes: SimpleChanges) {
 this.name = changes['childName'].currentValue;
 }
}

getter and setter in ES5 are used to monitor input attributes

In ES5, when we define the attributes of an object, we can set the associated getter and setter methods for the attributes of an object through Object. defineProperty methods. After we carry out this operation, the corresponding getter/setter methods will be called for preprocessing or changing the operation results for reading and assigning related attributes on the object.

By the same token, in Angular, we can intercept the changes of related values in the parent component and carry out specific data processing by setting and using the setter method of one input attribute.

This method is more intuitive, directly on the code:

Code implementation of the parent component:


import { Component } from '@angular/core';
@Component({
 moduleId: module.id,
 selector: 'name-parent',
 template: `
 <h2>Master controls {{names.length}} 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: module.id,
 selector: 'name-child',
 template: `<h3>"{{name}}"</h3>`
})
export class ChildComponent {
 name: string = 'default name';

 @Input()
 set name(name: string) {
 this.name = (name && name.trim()) || 'default name';
 }

 get name() { return this.name; }
}

Use @ Output to modify the attribute to realize child- > Communication between parent Components

In the new version of Angular, the communication between child components and parent components adopts the mechanism of coming events. This design is helpful to reuse components and decouple codes.
We don't need to care too much about the implementation of a component, we just need to know what data a component receives and what output events it generates.

Directly go to the code to intuitively understand 1:


@Component({
 moduleId: module.id,
 selector: 'child-component',
 template: `I'm {{ name }}`
})
export class ChildComponent {

 @Input() name: string;

 @Output() say: EventEmitter<boolean> = new EventEmitter<boolean>();

 ngOnChanges(changes: SimpleChange) {
 this.name = changes['childName'].currentValue;
 }

 speak() {
 this.say.emit(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: module.id,
 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() { 
 this.childName = 'StevenShen';
 }

 isChildSpeak(isIt: boolean) {
 console.log('The child speak status is %s', isIt ? 'ture' : 'false');
 }
}

In this way, the communication between parent and child components is realized.

However, such an implementation has a definite limitation: The parent component cannot use data binding to read the properties of the child component or call the methods of the child component.

Get the controller/template of the component through @ ViewChild for communication between components

In addition to using the @ Input and @ Output modifiers with the lifecycle hook function of Angular for inter-component communication. We can also use @ ViewChild to communicate between different components, not just between parent and child components. At the same time, using @ ViewChild, we can obtain more detailed component control permissions, such as reading the attribute values of child components or calling the methods of child components in the parent component. We still use the above code for transformation.

Changes to the ChildComponent component:


import { Component, OnInit } from '@angular/core';

import { DataService } from './shared/Dataservice';

@Component({
 moduleId: module.id,
 selector: '<my-component></my-component>',
 templateUrl: '.name.component.html',
 providers: [DataService]
})
export class MyComponent implements OnInit {

 constructor(private dataService: DataService) {}

 ngOnInit() {
 // Will invoke data handler everytime
 // when other component use the setter this.dataService.setData()
 this.dataService.getData().subscribe(data => console.log(data));
 }
}

0

Changes to the ParentComponent component:


import { Component, OnInit } from '@angular/core';

import { DataService } from './shared/Dataservice';

@Component({
 moduleId: module.id,
 selector: '<my-component></my-component>',
 templateUrl: '.name.component.html',
 providers: [DataService]
})
export class MyComponent implements OnInit {

 constructor(private dataService: DataService) {}

 ngOnInit() {
 // Will invoke data handler everytime
 // when other component use the setter this.dataService.setData()
 this.dataService.getData().subscribe(data => console.log(data));
 }
}

1

Through the above code transformation, we can also realize the communication between different components, and such component communication is not limited to the communication between parent and child components.

Summarize

Due to the technical level and time reasons, this article is roughly completed. The main arrangement is that I actually use some schemes in my work.


Related articles: