Is there a method to do communication between two

2020-02-15 09:07发布

问题:

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.



标签: angular