Sharing data between two unrelated components in Angular 4

I have an inquiry regarding passing data in Angular.

Unlike the usual structure of

<parent><child [data]=parent.data></child></parent>
, my structure is different:

<container>
  <navbar>
    <summary></summary>
    <child-summary><child-summary>
  </navbar>
  <content></content>
</container>

In the <summary /> section, I have a select element that sends values to both <child-summary /> and <content />.

The onSelect method is triggered correctly with (change) inside the <summary /> component.

I have attempted to use @Input, @Output, and @EventEmitter directives, but I am unable to retrieve the event as @Input of the component without following the parent/child pattern. All the examples I have come across show a direct relationship between components.

EDIT: Example using BehaviorSubject is not working as expected (all services connected to the API function properly, only the observable is fired at the start but not when the select value changes).

Shared service = company.service.ts (used for retrieving company data)

import { Injectable } from '@angular/core';
import { Headers, Http, Response } from '@angular/http';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/toPromise';

@Injectable()
export class SrvCompany {

    private accountsNumber = new BehaviorSubject<string[]>([]);
    currentAccountsNumber = this.accountsNumber.asObservable();

    changeMessage(accountsNumber: string[]) {
        this.accountsNumber.next(accountsNumber);
    }

    private _companyUrl = 'api/tiers/';

    constructor(private http: Http) { }

    getSociete(): Promise<Response> {
        let url = this._companyUrl;
        return this.http.get(url).toPromise();
    }
}

invoice.component.ts (the "child")

import { Component, OnInit, Input } from '@angular/core';
import { Headers, Http, Response } from '@angular/http';

import { SrvInvoice } from './invoice.service';
import { SrvCompany } from '../company/company.service';

@Component({
    selector: 'invoice',
    templateUrl: 'tsScripts/invoice/invoice.html',
    providers: [SrvInvoice, SrvCompany]
})

export class InvoiceComponent implements OnInit  {

    invoice: any;

    constructor(private srvInvoice: SrvInvoice, private srvCompany: SrvCompany)
    {
        
    }

    ngOnInit(): void {
        //this.getInvoice("F001");

        // Invoice data is linked to accounts number from company.
        this.srvCompany.currentAccountsNumber.subscribe(accountsNumber => {
            console.log(accountsNumber);
            if (accountsNumber.length > 0) {
                this.srvInvoice.getInvoice(accountsNumber).then(data => this.invoice = data.json());
            }
        });
    }

    //getInvoice(id: any) {
    //    this.srvInvoice.getInvoice(id).then(data => this.invoice = data.json());
    //}
}

company.component.ts (the triggering "parent")

import { Component, Inject, OnInit, Input } from '@angular/core';
import { Headers, Http, Response } from '@angular/http';

import { SrvCompany } from './company.service';

@Component({
    selector: 'company',
    templateUrl: 'tsScripts/company/company.html',
    providers: [SrvCompany]    
})

export class CompanyComponent implements OnInit {

    societes: any[];    
    soc: Response[]; // debug purpose
    selectedSociete: any;

    ville: any;
    ref: any;
    cp: any;
    accountNumber: any[];

    constructor(private srvSociete: SrvCompany)
    {

    }

    ngOnInit(): void {
        this.getSocietes();
    }

    getSocietes(): void {

        this.srvSociete.getSociete()
            .then(data => this.societes = data.json())
            .then(data => this.selectItem(this.societes[0].Id));
    }

    selectItem(value: any) {
        this.selectedSociete = this.societes.filter((item: any) => item.Id === value)[0];
        this.cp = this.selectedSociete.CodePostal;
        this.ville = this.selectedSociete.Ville;
        this.ref = this.selectedSociete.Id;
        this.accountNumber = this.selectedSociete.Accounts;
        console.log(this.accountNumber);
        this.srvSociete.changeMessage(this.accountNumber);
    }
}

Answer №1

If you have components structured as siblings and grandchildren, using a shared service is the way to go. I actually covered this scenario in a video where I demonstrate how to share data between components. Check it out!

To start, create a BehaviorSubject in the service:

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

@Injectable()
export class DataService {

  private messageSource = new BehaviorSubject("default message");
  currentMessage = this.messageSource.asObservable();

  constructor() { }

  changeMessage(message: string) {
    this.messageSource.next(message);
  }

}

Then, inject this service into each component and subscribe to the observable:

import { Component, OnInit } from '@angular/core';
import { DataService } from "../data.service";

@Component({
  selector: 'app-parent',
  template: `
    {{message}}
  `,
  styleUrls: ['./sibling.component.css']
})
export class ParentComponent implements OnInit {

  message:string;

  constructor(private data: DataService) { }

  ngOnInit() {
    this.data.currentMessage.subscribe(message => this.message = message);
  }

}

You can update the value from either component, and it will be reflected in both, even without a parent-child relationship:

import { Component, OnInit } from '@angular/core';
import { DataService } from "../data.service";

@Component({
  selector: 'app-sibling',
  template: `
    {{message}}
    <button (click)="newMessage()">New Message</button>
  `,
  styleUrls: ['./sibling.component.css']
})
export class SiblingComponent implements OnInit {

  message:string;

  constructor(private data: DataService) { }

  ngOnInit() {
    this.data.currentMessage.subscribe(message => this.message = message);
  }

  newMessage() {
    this.data.changeMessage("Hello from Sibling");
  }

}

Answer №3

There are a couple of ways to address this issue.

  1. One approach is to utilize shared services and observables.

  2. An alternative solution is to implement ngrx/store, which follows the Redux architecture. This allows you to access data from a centralized state.

Answer №5

If you're talking about components that are not related, it's likely they don't share a common parent component. However, if my assumption is wrong, you can check out another answer of mine addressing both scenarios.

In this situation, using an injectable service would be the way to go. Simply inject the service into each component and listen for its events.

(Similar to what is shown in the image below - source here - except we'll be injecting the service into two different Components)

https://i.sstatic.net/WpQmh.png

The documentation provides clear instructions on how to create and register an injectable service.

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

A solution for Array.includes to handle NaN values

While browsing some Javascript blogs, I stumbled upon the Array prototype methods .indexOf and .includes. I noticed that if an array includes NaN as a value, indexOf may not be able to detect it, leaving us with the option of using .includes. However, cons ...

Exploring SVG Morphing Reversal Techniques in Anime.js

I have been trying to implement direction: 'reverse' and timeline.reverse(), but so far it hasn't been successful. Interestingly, when I set loop: true, the reverse animation can be seen within the loop. However, my goal is to trigger this a ...

AWS Lambda Error: Module not found - please check the file path '/var/task/index'

Node.js Alexa Task Problem Presently, I am working on creating a Node.js Alexa Skill using AWS Lambda. One of the functions I am struggling with involves fetching data from the OpenWeather API and storing it in a variable named weather. Below is the relev ...

Adjusting the properties of an element with Javascript

My goal is to dynamically set the value of a parameter within a <script> element using JavaScript. I am using the Stripe checkout.js and I want to populate the Email input field with a value obtained from another text box on the page. Here's how ...

Exploring the world of React and Material Ui

As I delve into working with Material Ui in React, I am encountering difficulties when trying to customize the components. For instance, let's take a look at an example of the AppBar: import React from 'react'; import AppBar from 'mat ...

What causes my paragraph textContent to vanish after briefly displaying its value?

As a beginner in JavaScript and HTML, I am taking on the challenge of learning these languages from scratch independently. I have encountered an issue with my code where the word "Hi!" briefly flashes below the "Click Me!" button before disappearing compl ...

What is causing the Load More feature in jQuery to malfunction?

Attempting to create a basic "load more" feature using jquery. The concept is to mimic the functionality of traditional pagination where clicking loads additional posts from the database. Below is the javascript code: $(function(){ var count = 0; var ...

Question from Student: Can a single function be created to manage all text fields, regardless of the number of fields present?

In my SPFX project using React, TypeScript, and Office UI Fabric, I've noticed that I'm creating separate functions for each text field in a form. Is there a way to create a single function that can handle multiple similar fields, but still maint ...

Classify JavaScript Array Elements based on their Value

Organize array items in JavaScript based on their values If you have a JSON object similar to the following: [ { prNumber: 20000401, text: 'foo' }, { prNumber: 20000402, text: 'bar' }, { prNumber: 2000040 ...

Can Sync blocking IO be implemented solely in JavaScript (Node.js) without the use of C code?

Is JavaScript intentionally designed to discourage or disallow synchronous blocking I/O? What is the reason for the absence of a sleep API in JavaScript? Does it relate to the previous point? Can browsers support more than one thread executing JavaScript? ...

The input argument must be of type 'PollModel', as the property 'pollId' is required and missing in the provided 'any[]' type

Hey there! An issue popped up when I tried to pass an empty array as a model in my Angular project. The error message reads: "Argument of type 'any[]' is not assignable to parameter of type 'PollModel'. Property 'pollId' is ...

Vue's smooth scrolling in Nuxt.js was not defined due to an error with the Window

There seems to be an issue with adding vue smooth scroll to my nuxt.js project as I'm encountering the "window is not defined error". The steps I followed were: yarn add vue2-smooth-scroll Within the vue file, I included: import Vue from 'vue ...

Pressing the tab key makes all placeholders vanish

case 'input': echo '<div class="col-md-3"> <div class="placeholder"> <img src="images/person.png" /> &l ...

Troubleshooting EJS Relative Path Problem when Using "include" in an HTML Document

I'm encountering an issue with my "index.ejs" file... The current content of the ejs file: <!DOCTYPE html> <html lang="en" dir="ltr"> <!-- THIS SECTION IS FOR <head> TAG THAT WILL BE STORED INSIDE "so_ ...

How can I implement Javascript for tracking webshop activity and affiliate links across multiple websites?

I operate a small front end for a webshop where I receive 5% of the sale amount for every customer who makes a purchase after being referred from my website. However, I am struggling to track these customers and need help in setting up a system to monitor ...

Emphasize links with larger background panels compared to the link object, minus any padding

http://jsbin.com/OkaC/1/edit HTML: <ul> <li> <a class='active' href='#'>Link</a></li> <li><a href='#'>Link</a></li> </ul> CSS: .active { bac ...

Unable to locate repository instance while routing in the Express REST API

Can you please assist me with my routing issue? I am attempting to develop a REST API using TypeScript and the repository pattern in Node.js (express). I have created two generic classes, BaseRepository and BaseController, which handle basic CRUD transact ...

Storing retrieved data in React without using Redux involves using either the Context API or

When using Redux, I have a common store where fetched data is stored. For example, if I navigate away from my videos page (/videos) and then return to it, the videos are still available in the videos reducer. This allows me to show the user already loaded ...

Leverage npm JavaScript packages within an Ionic2 TypeScript project

Just diving into my first project with Ionic2 (TypeScript) and I'm trying to incorporate the npm JavaScript package. Specifically, I am utilizing https://github.com/huttarichard/instagram-private-api I'm a bit confused on how to correctly use im ...

Modifying the nginx configuration file for an Angular application: a step-by-step guide

After deploying my Angular application on an nginx server, I encountered a problem. When I accessed http://**.8.100.248:30749, it displayed the default index.html page from the nginx server instead of my actual application located in the html folder under ...