In angular we can communication between two components which are parent and child using @input and @output but is there a way to communicate between two components which not parent and child without using event emitter?
问题:
回答1:
1) Yes, as stated in the documentation, you can in fact make use of services for component interaction. They don't have to be related (parent-child). Check out the above link for a sample implementation of it.
2) Another way to do it with services would be the following way below, but it will make use of EventEmitter:
@Injectable()
export class CommunicationService {
isTrigger: boolean = false;
@Output() change: EventEmitter<boolean> = new EventEmitter();
toggle() {
this.isTrigger = !this.isTrigger;
this.change.emit(this.isTrigger);
}
}
On the 'parent' component,
// somewhere in this component will call the emit() method
.
.
emit() {
this.communicationService.toggle();
}
On the target component,
isTrigger: boolean = false
.
.
this.communicationService.change.subscribe(isTrigger => {
this.isTrigger = isTrigger;
});
3) State management. Depending on the complexity of your project, it may be better to use a state management framework such as NgRx, as your main components will be much cleaner. If you have worked with React/Redux, this will be very familiar to you. I will only recommend it if your application has a real requirement for it. Otherwise, a combination of Observables/RxJS and EventEmitters will be more than sufficient.
回答2:
You can also archive this by using a subject from Rxjs
1./ Create service
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class MessageService {
public message = new Subject<string>();
setMessage(value: string) {
this.message.next(value); //it is publishing this value to all the subscribers
that have already subscribed to this message
}
}
2./ Now, inject this service in component1.ts and pass an instance of it to the constructor. Do this for component2.ts too. Use this service instance for passing the value of #message to the service function setMessage
import { Component } from '@angular/core';
import { MessageService } from '../../service/message.service';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class Component1Component {
constructor(public messageService:MessageService) { }
setMessage(event) {
console.log(event.value);
this.messageService.setMessage(event.value);
}
}
3./ Inside component2.ts, subscribe and unsubscribe (to prevent memory leaks) to the Subject
import { Component, OnDestroy } from '@angular/core';
import { MessageService } from './service/message.service';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class Component2Component {
message: string;
subscription: Subscription;
constructor(public messageService: MessageService) { }
ngOnInit() {
this.subscription = this.messageService.message.subscribe(
(message) => {
this.message = message;
}
);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
回答3:
A solution using Observables.
//message.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class MessageService {
private messageCommand = new Subject<string>();
messageCommand$ = this.messageCommand.asObservable();
invokeMessage(msg: string) {
this.messageCommand.next(msg);
}
}
//component-one.ts
import { Component, OnInit } from '@angular/core';
import { MessageService } from '../services/message.service';
@Component({
selector: 'app-component-one',
templateUrl: './component-one.component.html',
styleUrls: ['./component-one.component.css']
})
export class ComponentOneComponent implements OnInit {
constructor(private messageService: MessageService) { }
ngOnInit() {
}
yourActionMethod() {
this.messageService.invokeMessage('This is from component one');
}
}
//component-two.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { MessageService } from '../services/message.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-component-two',
templateUrl: './component-two.component.html',
styleUrls: ['./component-two.component.css']
})
export class ComponentTwoComponent implements OnInit, OnDestroy {
messageSubscription: Subscription;
message: string;
constructor(private messageService: MessageService) { }
ngOnInit() {
this.subscribeToMessageEvents();
}
ngOnDestroy(): void {
this.messageSubscription.unsubscribe();
}
subscribeToMessageEvents() {
this.messageSubscription = this.messageService.messageCommand$.subscribe(
(msg: string) => {
this.message = msg;
}
);
}
}
Here I have used a service class containing an observable of type string.
Then from component-one, a message is published using the invokeMessage method in our message service.
The component which needs to receive the message, in our case component-two should subscribe to the messsageCommand$ in the message service.
One thing you have to keep in mind is whenever you are subscribing to an observable make sure that you unsubscribe it when the component gets destroyed.