Во-первых, что вам нужно, чтобы понять отношения между компонентами. Тогда вы можете выбрать правильный способ общения. Я попытаюсь объяснить все методы, которые я знаю и использую в своей практике для взаимодействия между компонентами.
Какие могут быть отношения между компонентами?
1. Родитель> Ребенок
Обмен данными через ввод
Это, наверное, самый распространенный способ обмена данными. Он работает с помощью @Input()
декоратора, чтобы данные могли быть переданы через шаблон.
parent.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'parent-component',
template: `
<child-component [childProperty]="parentProperty"></child-component>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent{
parentProperty = "I come from parent"
constructor() { }
}
child.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'child-component',
template: `
Hi {{ childProperty }}
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
@Input() childProperty: string;
constructor() { }
}
Это очень простой метод. Это простой в использовании. Мы также можем отслеживать изменения данных в дочернем компоненте с помощью ngOnChanges .
Но не забывайте, что если мы используем объект в качестве данных и изменяем параметры этого объекта, ссылка на него не изменится. Следовательно, если мы хотим получить модифицированный объект в дочернем компоненте, он должен быть неизменным.
2. Ребенок> Родитель
Обмен данными через ViewChild
ViewChild позволяет вводить один компонент в другой, предоставляя родительскому доступу к его атрибутам и функциям. Однако одно предостережение заключается в том, что child
он не будет доступен до тех пор, пока представление не будет инициализировано. Это означает, что нам нужно реализовать хук жизненного цикла AfterViewInit для получения данных от дочернего элемента.
parent.component.ts
import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { ChildComponent } from "../child/child.component";
@Component({
selector: 'parent-component',
template: `
Message: {{ message }}
<child-compnent></child-compnent>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent implements AfterViewInit {
@ViewChild(ChildComponent) child;
constructor() { }
message:string;
ngAfterViewInit() {
this.message = this.child.message
}
}
child.component.ts
import { Component} from '@angular/core';
@Component({
selector: 'child-component',
template: `
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
message = 'Hello!';
constructor() { }
}
Совместное использование данных через Output () и EventEmitter
Другим способом обмена данными является отправка данных от дочернего элемента, которые могут быть перечислены родителем. Этот подход идеален, когда вы хотите поделиться изменениями данных, которые происходят при таких вещах, как нажатие кнопок, записи форм и другие пользовательские события.
parent.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'parent-component',
template: `
Message: {{message}}
<child-component (messageEvent)="receiveMessage($event)"></child-component>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent {
constructor() { }
message:string;
receiveMessage($event) {
this.message = $event
}
}
child.component.ts
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'child-component',
template: `
<button (click)="sendMessage()">Send Message</button>
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
message: string = "Hello!"
@Output() messageEvent = new EventEmitter<string>();
constructor() { }
sendMessage() {
this.messageEvent.emit(this.message)
}
}
3. Братья и сестры
Ребенок> Родитель> Ребенок
Я пытаюсь объяснить другие способы общения между братьями и сестрами ниже. Но вы уже могли понять один из способов понимания вышеуказанных методов.
parent.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'parent-component',
template: `
Message: {{message}}
<child-one-component (messageEvent)="receiveMessage($event)"></child1-component>
<child-two-component [childMessage]="message"></child2-component>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent {
constructor() { }
message: string;
receiveMessage($event) {
this.message = $event
}
}
дети-one.component.ts
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'child-one-component',
template: `
<button (click)="sendMessage()">Send Message</button>
`,
styleUrls: ['./child-one.component.css']
})
export class ChildOneComponent {
message: string = "Hello!"
@Output() messageEvent = new EventEmitter<string>();
constructor() { }
sendMessage() {
this.messageEvent.emit(this.message)
}
}
дети-two.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'child-two-component',
template: `
{{ message }}
`,
styleUrls: ['./child-two.component.css']
})
export class ChildTwoComponent {
@Input() childMessage: string;
constructor() { }
}
4. Несвязанные компоненты
Все методы, которые я описал ниже, могут быть использованы для всех вышеупомянутых опций для связи между компонентами. Но у каждого есть свои преимущества и недостатки.
Обмен данными с сервисом
При передаче данных между компонентами, которые не имеют прямого соединения, такими как братья и сестры, внуки и т. Д., Вы должны использовать общую службу. Когда у вас есть данные, которые всегда должны быть синхронизированы, я считаю, что RxJS BehaviorSubject очень полезен в этой ситуации.
data.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable()
export class DataService {
private messageSource = new BehaviorSubject('default message');
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
first.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from "../data.service";
@Component({
selector: 'first-componennt',
template: `
{{message}}
`,
styleUrls: ['./first.component.css']
})
export class FirstComponent implements OnInit {
message:string;
constructor(private data: DataService) {
// The approach in Angular 6 is to declare in constructor
this.data.currentMessage.subscribe(message => this.message = message);
}
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
}
second.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from "../data.service";
@Component({
selector: 'second-component',
template: `
{{message}}
<button (click)="newMessage()">New Message</button>
`,
styleUrls: ['./second.component.css']
})
export class SecondComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
newMessage() {
this.data.changeMessage("Hello from Second Component")
}
}
Обмен данными с маршрутом
Иногда вам нужно не только передавать простые данные между компонентами, но и сохранять некоторое состояние страницы. Например, мы хотим сохранить фильтр на онлайн-рынке, а затем скопировать эту ссылку и отправить другу. И мы ожидаем, что он откроет страницу в том же состоянии, что и мы. Первый и, возможно, самый быстрый способ сделать это - использовать параметры запроса. .
Параметры запроса более похожи на то, /people?id=
где id
может быть что угодно, и вы можете иметь столько параметров, сколько захотите. Параметры запроса будут разделены символом амперсанда.
При работе с параметрами запроса вам не нужно определять их в файле маршрутов, и они могут называться параметрами. Например, возьмите следующий код:
page1.component.ts
import {Component} from "@angular/core";
import {Router, NavigationExtras} from "@angular/router";
@Component({
selector: "page1",
template: `
<button (click)="onTap()">Navigate to page2</button>
`,
})
export class Page1Component {
public constructor(private router: Router) { }
public onTap() {
let navigationExtras: NavigationExtras = {
queryParams: {
"firstname": "Nic",
"lastname": "Raboy"
}
};
this.router.navigate(["page2"], navigationExtras);
}
}
На странице получения вы получите эти параметры запроса, такие как:
page2.component.ts
import {Component} from "@angular/core";
import {ActivatedRoute} from "@angular/router";
@Component({
selector: "page2",
template: `
<span>{{firstname}}</span>
<span>{{lastname}}</span>
`,
})
export class Page2Component {
firstname: string;
lastname: string;
public constructor(private route: ActivatedRoute) {
this.route.queryParams.subscribe(params => {
this.firstname = params["firstname"];
this.lastname = params["lastname"];
});
}
}
NgRx
Последний способ, который является более сложным, но более мощным, заключается в использовании NgRx . Эта библиотека не для обмена данными; это мощная государственная библиотека управления. Я не могу в коротком примере объяснить, как его использовать, но вы можете зайти на официальный сайт и прочитать документацию об этом.
Для меня NgRx Store решает несколько проблем. Например, когда вам приходится иметь дело с наблюдаемыми и когда ответственность за некоторые наблюдаемые данные распределяется между различными компонентами, действия хранилища и редуктор гарантируют, что изменения данных всегда будут выполняться «правильным образом».
Он также обеспечивает надежное решение для кэширования HTTP-запросов. Вы сможете хранить запросы и их ответы, чтобы убедиться, что на ваш запрос еще не сохранен ответ.
Вы можете прочитать о NgRx и понять, нужно ли вам это в вашем приложении или нет:
Наконец, я хочу сказать, что перед выбором некоторых методов обмена данными вам необходимо понять, как эти данные будут использоваться в будущем. Я имею в виду, может быть, только сейчас вы можете использовать только @Input
декоратор для обмена именем пользователя и фамилией. Затем вы добавите новый компонент или новый модуль (например, панель администратора), который нуждается в дополнительной информации о пользователе. Это означает, что это может быть лучшим способом использования службы для пользовательских данных или каким-либо другим способом обмена данными. Вы должны подумать об этом больше, прежде чем начать осуществлять обмен данными.