У меня есть два компонента следующим образом, и я хочу вызвать функцию из другого компонента. Оба компонента включены в третий родительский компонент с помощью директивы.
Ответ 2
Во-первых, что вам нужно, чтобы понять отношения между компонентами. Тогда вы можете выбрать правильный способ общения. Я попытаюсь объяснить все методы, которые я знаю и использую в своей практике для взаимодействия между компонентами.
Какие могут быть отношения между компонентами?
1. Родитель> Ребенок
![enter image description here]()
Обмен данными с помощью ввода
Это, наверное, самый распространенный способ обмена данными. Он работает с помощью декоратора @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. Ребенок> Родитель
![enter image description here]()
Обмен данными через 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. Братья, сестры
![enter image description here]()
Ребенок> Родитель> Ребенок
Я пытаюсь объяснить другие способы общения между братьями и сестрами ниже. Но вы уже могли понять один из способов понимания вышеуказанных методов.
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. Несвязанные компоненты
![enter image description here]()
Все методы, которые я описал ниже, могут быть использованы для всех вышеупомянутых опций для связи между компонентами. Но у каждого есть свои преимущества и недостатки.
Обмен данными с сервисом
При передаче данных между компонентами, которые не имеют прямого соединения, такими как братья и сестры, внуки и т.д., Вы должны использовать общую службу. Когда у вас есть данные, которые всегда должны быть синхронизированы, я считаю, что 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
для совместного использования имени пользователя и фамилии. Затем вы добавите новый компонент или новый модуль (например, панель администратора), который нуждается в дополнительной информации о пользователе. Это означает, что это может быть лучшим способом использования службы для пользовательских данных или каким-либо другим способом обмена данными. Вы должны подумать об этом больше, прежде чем начать осуществлять обмен данными.