简体   繁体   中英

Is there a method to do communication between two components which are not parent and child without using event emitter in angualr?

在angular中,我们可以使用@input和@output在父级和子级两个组件之间进行通信,但是有没有一种方法可以在不使用事件发射器的情况下在父级和子级两个组件之间进行通信?

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();
 }
}

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM