How can Angular components communicate with each other through a shared service?

Recently, I dived into learning Angular and came across an interesting example in the official documentation discussing parent-child communication using a service:

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable()
export class MissionService {

  // Observable string sources
  private missionAnnouncedSource = new Subject<string>();
  private missionConfirmedSource = new Subject<string>();

  // Observable string streams
  missionAnnounced$ = this.missionAnnouncedSource.asObservable();
  missionConfirmed$ = this.missionConfirmedSource.asObservable();

  // Service message commands
  announceMission(mission: string) {
    this.missionAnnouncedSource.next(mission);
  }

  confirmMission(astronaut: string) {
    this.missionConfirmedSource.next(astronaut);
  }
}

One particular section of the code puzzled me:

  // Observable string streams
  missionAnnounced$ = this.missionAnnouncedSource.asObservable();
  missionConfirmed$ = this.missionConfirmedSource.asObservable();

I understand that .asObservable() creates a new Observable with the Subject as the source. But why do we need it? Isn't missionAnnouncedSource already an observable on its own? Why not simplify the service like this:

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable()
export class MissionService {

  // Observable string sources
   missionAnnouncedSource = new Subject<string>();
   missionConfirmedSource = new Subject<string>();

  // Service message commands
  announceMission(mission: string) {
    this.missionAnnouncedSource.next(mission);
  }

  confirmMission(astronaut: string) {
    this.missionConfirmedSource.next(astronaut);
  }
}

While adjustments would be needed in components, I fail to see any drawbacks:

//astronaut.component.ts

constructor(private missionService: MissionService) {
    this.subscription = missionService.missionAnnouncedSource.subscribe(
      mission => {
        this.mission = mission;
        this.announced = true;
        this.confirmed = false;
    });
}

Is this following a certain convention or am I overlooking something?

Answer №1

While it's not necessary, the concept behind this is that by only exposing an observable, you prevent outside code from directly calling next on the public variable - instead, it should be handled internally within the service.

Answer №2

After having the same question, I decided to look into it and here's what I discovered:

According to the RxJS documentation:

A Subject acts like an Observable but can broadcast to multiple Observers. Subjects operate similarly to EventEmitters in that they maintain a list of several listeners.

Essentially, you can achieve the same functionality with Observables by using the next(value) method to modify the value.

I found this article helpful as it discusses the concept of best practices. Here's an example from the article:

In the world of RxJS, it's recommended to only expose Subjects to parts of your application responsible for adding new data into the Observable sequence. This aligns with the idea of restricting write access in various aspects of your application, such as private and public class members.

To convert a Subject into an Observable, you simply need to call asObservable() on any Subject instance:

let currentUserSubject$ = new BehaviorSubject() < string > 'Eric';
let currentUser$ = currentUserSubject$.asObservable();

Now, we have a new variable called currentUser$, which represents an Observable of the observable sequence generated by currentUserSubject$. To better understand this concept, let's subscribe to the currentUser Observable and add some data to currentUserSubject$:

let currentUserSubject$ = new BehaviorSubject() < string > 'Eric';
let currentUser$ = currentUserSubject$.asObservable();

currentUserSubject$.subscribe(val => {
  console.log(val);
});
// => 'Eric'

currentUserSubject$.next('hello');
// => 'hello'

It's important to note that attempting to use next() on currentUser$ will result in an error because Observables are designed for observing sequences, giving you read-only access to currentUserSubject!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

After importing RxJS, the error "toPromise is not a function" is encountered

I am trying to utilize the Promise operator Firstly, I imported rxjs: import 'rxjs/add/operator/toPromise'; then in my component, I implemented it ngOnInit() { this.EJnames= this.dataservice.getResults(); this.generalinfosservice.get ...

Learn how to send or submit data using the Form.io form builder

I am currently working on a dynamic drag and drop form builder, and I'm struggling to figure out how to log the data from the form. Below is the component.html file where I am implementing the drag and drop form: <div> <form-builder ...

The connection between Cognito identity and Mqtt is not possible within AWS IoT Core

The application is built using Angular. Upon logging in with Cognito, the application retrieves the user's CognitoUser data, including id token, access key, and session token. Next, the application connects to Iot Core to subscribe to or publish data ...

Error in Angular 2 component when loading background images using relative URLs from an external CSS skin

In my angular2 component, I am utilizing a third-party JavaScript library. The skin CSS of the component attempts to load images using relative URL paths. Since I am following a component-based architecture, I prefer to have all component dependencies enca ...

Testing Angular 2 components with material icons and images

Recently, I finished creating a unique component that showcases an image, material icons, and a custom directive known as ticker. This directive allows for scrolling text if it exceeds the width of the element. https://i.stack.imgur.com/GpDSr.png My next ...

Locate a specific item by its ID within a JSON file utilizing Angular version 2 or later

My JSON file structure is like the example below: { "id": "1", "country": "Brazil", "state": [ {"id": "1", "name": "Acre", "city": [ { "id": "1", "name": "Rio Branco"}, { "id": "2", "name": "Xapuri"} ...

Can you explain the significance of syntax in sample code (typescript, react)?

const sampleFunction: (inputString: string) => string = inputString => { return inputString.split(""); } I'm a bit confused about the code below and would appreciate some clarification. I understand that only "string" as a type is accepted, b ...

Go to a specific component located in a different module within Angular

I have a default app.component that contains a button. When this button is clicked, I want to navigate to the login.component. Below is a snippet from my app.module.ts file: import { BrowserModule } from '@angular/platform-browser'; ...

Error encountered by Angular's Injector in the AppModule when attempting to access the HttpHandler component

I have been successfully running an app for the past few months with no issues. Now, I am exploring the idea of moving some common services into a library that can be utilized by other applications. For this project, I decided to avoid using Angular CLI t ...

Void animations in Angular 2 fail to trigger upon removal

How can I trigger animations when removing a DOM element in Angular2? import { Component, Input, Output, EventEmitter } from '@angular/core'; import { FormGroup, FormControl, Validators } from "@angular/forms"; import { trigger, state, st ...

How can I detect the shift key press when an array key is pressed in Angular 2?

I have a collection of items that I want to implement file traversal behavior for, similar to a file explorer. This means that after selecting an item, if you hold down the shift key and press the down arrow, those items should also be selected. Below is ...

"Exploring the process of creating a custom type by incorporating changes to an existing interface

One of the challenges I'm facing is defining an object based on a specific interface structure. The interface I have looks like this: interface Store { ReducerFoo : ReducerFooState; ReducerBar : ReducerBarState; ReducerTest : ReducerTestSt ...

Opening the Gmail app from a link using JavaScript

What is the best way to open the Gmail app from a hyperlink? This link opens WhatsApp <a href="https://wa.me/">whatsapp</a> <a href="mailto:<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="6a1f190f ...

typescript exploring the versatility of dynamic types and generics

Understanding TypeScript dynamic and generic types can be challenging for me. The goal is to create a function that generates an object with a specific type, where some properties of the object must match the parameters provided to the function. Essentia ...

Are MobX Observables interconnected with RxJS ones in any way?

Is the usage of RxJs observables in Angular comparable to that in React and MobX? I'm struggling to find information on this topic. ...

Managing Deactivated Elements in Ionic Web Edition

In my extensive ionic application that I deploy to both the Web and mobile platforms, a member of the testing team brought to my attention an issue regarding disabled elements. I have numerous elements such as buttons, checkboxes, inputs, etc., which can b ...

Troubleshooting display glitches on Bootstrap modals in Internet Explorer 11 and Microsoft Edge

I have been encountering rendering artifacts consistently across various versions of IE11 and Edge, on different devices with varying graphics cards and drivers, as well as different update statuses of Windows 10. The screenshots indicate some of these ar ...

Screen remains blank as the React webpage fails to load

Hello, I am having issues with my React page not showing up. Can someone please review my code to see if there are any errors? Here is the edited program: index.html <!doctype html> <html lang="en"> <head> <meta charset ...

What is the process for generating a fresh instance of an Angular service?

Hey there, I've been pondering a solution to a little dilemma I'm facing. Let me share my thoughts and see if you can lend some insight or tell me where I might be going astray. Setting the Stage: In the realm of angular app creation, it's ...

challenge communicating between Angular and Node using CORS plugin

I've been researching how to enable CORS in node/express and have tried implementing various solutions, but without any success. Here is my angular request: function getPhotos(location) { var url = 'https://api.instagram.com/v1/media/sear ...