Transferring information between components, specifically when one of them is a routerOutlet within an Angular application

I need to transfer data from the category component to the product component within the dashboard component. However, I am facing an issue due to the presence of a router outlet inside the product component.

View Dashboard Screen

dashboard.component.html

<div class="columns">
    <div class="column is-2">
        <app-category></app-category>
    </div>
    <div class="column is-10">
        <router-outlet ></router-outlet>
    </div>
</div>

category.component.html

<div class="container">
    <input type="checkbox" id="menu-toggle">
    <label for="menu-toggle" class="hamburger-menu" (click)="showCategoryList()"><span></span></label>
    <div class="content">
        <p class="menu-title title is-4" style="font-weight: normal">Categories</p>
        <ul class="menu-list">
            <li class="subtitle category-list" *ngFor="let category of categories">
                <input type="checkbox" [id]="category.id" (change)="filterProductsByCategory($event)">
                <label [htmlFor]="category.id">{{category.name}}</label>
            </li>
        </ul>
        <div class="filter mb-3"><button (click)="getFilterData()">Filter</button></div>
    </div>
</div>

category.component.ts

export class CategoryComponent implements OnInit{

  categories: Category[] = [];
  activatedCategory?: Category;
  dataLoaded = false;
  currentCategories: Category[] = [];
  selectedCategory: Category[]  = [];

  constructor(private categoryService: CategoryService) { }

  hamburgerMenu: HTMLElement = document.querySelector(".hamburger-menu")!;
  ngOnInit(): void {
    this.getCategories();
   
  }

  getCategories() {
    this.categoryService.getCategories().subscribe(response => {
      this.categories = response.data;
      this.dataLoaded = true;
    })
  }
  
  getActivatedCategory(category: Category) {
    this.activatedCategory = category;
  }

  setCurrencyCategory(category: Category) {
    this.currentCategories.push(category);
  }

  showCategoryList() {
    var content = document.querySelector(".content")! as HTMLElement;
    if (content.style.display === "block") {
      content.style.transition = "0.5s ease";
      content.style.display = "none";
    }
    else {
      content.style.transition = "0.5s ease";
      content.style.display = "block";
    }
  }

  filterProductsByCategory(event: any){
    if(event.target.checked){
      console.log(event.target.id);
      this.selectedCategory.push(event.target.id);
    }else{
      const id = event.target.id;
      this.selectedCategory.forEach((category, index) => {
        if(category.id === id){
          const index = this.selectedCategory.indexOf(category);
          console.log(index);
          this.selectedCategory.splice(index, 1);
        }
      })
    }
  }

  getFilterData(){
    const pars = this.selectedCategory.map((category: any) => {
      return parseInt(category);
    })
    console.log(this.selectedCategory);
  }
}

products.component.html

<div id="products">   
    <div *ngIf="!dataLoaded" class="lds-ring"><div></div><div></div><div></div><div></div></div>
    <h1 class="title">Products</h1>
    <div id="search-box">
      <input class="input mb-4" type="text" placeholder="Filter by product name" [(ngModel)]="searchText">
      <button class="button is-dark" type="button">Search</button>
    </div>
    
    <table *ngIf="dataLoaded && products" class="table is-hoverable is-fullwidth  is-striped" >
      <thead>
        <tr>
            <th>ID</th>
            <th>Product Name</th>
            <th>Category ID</th>
            <th>Price</th>
            <th>Unit In Stock</th>
            <th>Unit On Order</th>
            <th>Reorder Level</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let product of products | filterProducts: searchText">
          <td>{{ product.id }}</td>
          <td>{{ product.name }}</td>
          <td>{{ product.categoryID }}</td>
          <td>{{ product.unitPrice| currency: "TR" : '₺' : '2.2-2'  }}</td>
          <td>{{ product.unitsInStock }}</td>
          <td>{{ product.unitsOnOrder }}</td>
          <td>{{ product.reorderLevel }}</td>
        </tr>
    </table>
    <div *ngIf="!products">No Products!</div>
  </div>

products.component.ts

export class ProductsComponent implements OnInit{

  products: Product[] = [];
  searchText = '';

  dataLoaded = false;
  constructor(private productService: ProductService, private activatedRoute: ActivatedRoute) { }

  ngOnInit(): void {
    this.activatedRoute.params.subscribe(params => {
      if(params["id"]){
        this.getProductsByCategoryId(params["id"])
      }else{
        this.getProducts();

      }
    })
  }

  getProducts() {
    this.productService.getProducts().subscribe(response => {
        this.products = response.data;
        this.dataLoaded = true; 
    })
   }

   getProductsByCategoryId(categoryId: number) {
    this.productService.getProductsByCategoryId(categoryId).subscribe(response => {     
        this.products = response.data;
        this.dataLoaded = true;
    })
   }  
}

I am looking for a way to easily share data between the categories and products components

Answer №1

Create a service with providedIn: 'root' to allow the entire application access to the service and make sure to add it to the providers array.

Utilize this service to set and read data efficiently for passing between components!

To call methods from one component to another, utilize a Subject.

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

@Injectable({
  providedIn: 'root'
})
export class ShareService {
  dataToShare = null;
  subject: Subject<any> = new Subject<any>();
  subObs$: Observable<any>;

  constructor() {
      this.subObs$ = this.subject.toObservable();
  }

  emit(eventName: string, data = {}) {
      this.subject.next({eventName: eventName, data: data});
  }
}

Calling Methods Between Components

By using the following code, we can call methods between unrelated components by emitting from one location and subscribing in another. Using simple if statements, we filter out the methods - this concept is known as an event bus. Remember to unsubscribe from the subscription on component destroy to prevent memory leaks!

Component A

callBMethod() {
    this.shareService.emit('compBEvent', {});
}

Component B

ngOnInit() {
    this.shareService.subObs$.subscribe(({eventName, data}) => {
        if(eventName === 'compBEvent'){
             this.bMethod();
        }
    });
}

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

Setting a value in Ionic 3 HTML template

Attempting to assign a value in an Ionic 3 template from the ts file while also adding css properties but encountered an issue. PROBLEM Error: Uncaught (in promise): Error: No value accessor for form control with name: 'image' Error: No va ...

Mastering Data Labels in ng2-chart: A step-by-step guide

Once again, I find myself battling my Angular and JavaScript challenges, each question making me feel a little less intelligent. Let me walk you through how I got here. In my most recent project, I wanted to enhance the user experience by incorporating sl ...

What is the best way to troubleshoot a quasar typescript file type error?

Currently, I am delving into Quasar using TypeScript and encountering a type error while working on file uploads. Here is the snippet of my code where the type error arises specifically in the parameter of the form.append() method. The error message read ...

What is the recommended TypeScript type for the NextJS _app.tsx Component and pageProps?

Take a look at the default _app.tsx code snippet from NextJS: function MyApp({ Component, pageProps }) { return ( <Component {...pageProps} /> ) } The issue arises when transitioning to TypeScript, as ES6Lint generates a warning indicating t ...

Discover the best practices for implementing services within the import decorator of an angular module configuration

Is there a way to access a service inside the load module in Angular, especially when the module is loaded from a decorator and the service is not yet DI? You can refer to this answer for an example. For instance, if the method in the service returns an ...

Removing a loaded stylesheet in Angular 4

One of the functions in my codebase is responsible for loading a stylesheet based on the user's language choice, whether it's 'en' or 'ar': addStyleSheet(lang: string) { let headID = document.getElementsByTagName(&apo ...

Guide to sending client-to-client notifications in Angular/Ionic with Firebase Cloud Messaging

I am looking to implement client-client push notifications (not server-to-client). My goal is to send a notification when one user messages another. Is this feasible? How can I achieve this using the structure in the Firebase real-time database? Here is a ...

Guide on upgrading an Angular project to a targeted version with its corresponding dependencies

I'm embarking on reviving a previous angular venture. My objective is to bring it up-to-date with a particular version along with upgrading all its affiliated dependencies to the most recent ones. I attempted by initially uninstalling the CLI version, ...

Service provider not found at Injection Error and No provider error occurred

error message I am a newcomer to Angular 2. I keep encountering the "No provider for service at injection error" and "no provider error" even though I have specified the provider in the app module. The code is cribs.service.ts import { Injectable } from ...

The recent update from Angular version 5.2 to 7 has caused issues with Post methods

An issue has occurred with the type mismatch in the error handling function. It seems that the argument provided is not compatible with the expected parameter type within the Observable structure. GetFullAddress(addressModel: FullAddr ...

Utilizing REST-API with Angular 2 and Electron

I am encountering an issue with my Electron App that utilizes Angular 2. I had to make a modification from <base href="/"> to <base href="./">, which is a relative path within the file system, in order to make it function properly. However, thi ...

The Angular 2 Final Release is encountering an issue where it is unable to locate the module name with the

Recently, I made the transition to Angular 2 Final Release from RC 4 and encountered an issue with an error message cannot find name 'module' in my code: @Component({ selector: 'dashboard', moduleId: module.id, templateUrl: ...

Utilize Angular 4 to dynamically load templates within a single component

My task is to create a component with multiple HTML templates, each containing at least 20 controls. Depending on certain conditions, a specific template should be loaded. Note: I have opted for 3 different templates because the controls vary based on the ...

Setting Authorization with username, password, and domain in Angular 2 HTTP Request

I am facing an issue with calling an API method hosted on another machine using Angular 2 component with Http. When accessing the API from a browser, I can connect by entering a username and password as shown below: https://i.stack.imgur.com/JJqpC.png Ho ...

Encountering errors while attempting to utilize Ionic plugins within a nxtend/ionic-angular project

I've been trying to integrate nxtend/ionic-angular into my project, but I encountered issues with using plugins. Here are the two use cases where I faced problems: 1st Use Case: I needed to retrieve the current application version, so I installed the ...

How do I fix the build error that says "Operator '+' cannot be used with types 'number[]'?

The function below is designed to generate unique uuidv4 strings. function uuidv4() { return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => ( c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)) ...

The installation of bower angular-card-input failed during the execution of the Jenkins build script due to an HTTP request

I encountered an issue when trying to run a bower script for building a frontend in Angular: When executing the bower command, I received an error regarding git fetch from the following link: https://github.com/angular-ui/ui-utils.git, with exit code ...

Having difficulty deciphering the legend in the Highcharts library for Angular (angular-highcharts)

I have a requirement to display two datasets as dual column charts. (2) [{…}, {…}] 0: historyDate: "2021-02-10T10:00:000Z" documentStatusHistory: CANCELLED: 6 COMPLETED: 52 IN_PROGRESS: 1 OPEN: 1 ...

What is the purpose of mapping through Object.keys(this) and accessing each property using this[key]?

After reviewing this method, I can't help but wonder why it uses Object.keys(this).map(key => (this as any)[key]). Is there any reason why Object.keys(this).indexOf(type) !== -1 wouldn't work just as well? /** * Checks if validation type is ...

An unexpected error has occurred: Uncaught promise rejection with the following message: Assertion error detected - The type provided is not a ComponentType and does not contain the 'ɵcmp' property

I encountered an issue in my Angular app where a link was directing to an external URL. When clicking on that link, I received the following error message in the console: ERROR Error: Uncaught (in promise): Error: ASSERTION ERROR: Type passed in is not Co ...