2

my understanding of Angular is very basic (I'm trying to learn!) and need some help with regards to setting a click event between 2 separate components. I'll specify the scenario...

I have a header menu which contains a button which on click, will expand and collapse a side menu. The header menu and side menu are separate components. I need to basically toggle a class inside the sidebar component when the button in the header component is clicked

I've read that it might be best to create a service (rxjs/BehaviorSubject) that links the two components together however I've never created one before so not really sure how to proceed.

Is anyone able to assist? Thanks in advance!

  • You need to create one Subject which stores your className/ variable , On click of button , you have to change the value of Subject , In the menu Component, you have to subscribe the value of subject in the ngOnOnit function Based on the variable value you can toggle the class. – rishabh tripathi Jul 15 '22 at 16:57
  • There are many ways to solve this problem. Service+RxJS is a very elegant solution, yet perhaps a bit much for a beginner. Start simple. Create a service, and have it injected in both components. Give that service a public property, which holds the current CSS class. In the header menu, update the Service's property. Use the property directly in your side menu's template. I believe that should work. Progress to RxJS and Subjects from there. To create the service, you can use the CLI (ng c service I believe) – NoBullsh1t Jul 15 '22 at 17:12

1 Answers1

3

As per Software Engineering prospective, identify the problem first, then think of the solution within the scope of design patterns, but also take in consideration the framework. In the mentioned scenario; the Observer Pattern is what you are looking for.

We need to communicate between components that might or might not be located within the same module which also might have a parent-child or child-parent relationship, or might be brothers-relationship or might not have a relationship at all! So the generic solution is to created a Service.

Now according to your use case, you're calling it as if you have this one only functionality in which you want to observe this button to toggle the sidenav, but from my own point of view, it's better to create a more generic form of usage in which is defined as "Communication". This will make your service reusable and can do many other things than just observing a button.

I'll add some code to demonstrate.

First of all we've got the communication.service.ts

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

@Injectable({
  providedIn: 'root', // Singleton
})
export class ComunicationService {
  private subject: Subject<any>;
  private observable$: Observable<any>;

  constructor() {
    this.subject = new Subject();
    this.observable$ = this.subject.asObservable();
  }

  sendData(data: any) {
    this.subject.next(data);
  }

  clearData() {
    this.subject.next();
  }

  getData(): Observable<any> {
    return this.observable$;
  }
}

Now in the header.component.ts for example we need to use DI design pattern to inject this service and use it to sendData.

export class HeaderComponent {
  constructor(private communicationService: CommunicationService) {}

  onButtonClick() {
    this.communicationService.sendData(ANYTHING_YOU_WANT_TO_SHARE);
  }
}

And in your sidenav.component.ts

export class SidenavComponent {
  constructor(private comunicationService: CommunicationService) {
    this.listenToButtonClicks(); // Never forget to call the subscribing function in the constructor, and unsubscribe OnDestroy to prevent memory leak
  }

  listenToButtonClicks() {
    this.communicationService.getData().subscribe((res) => {
      console.log(res); // Check if you're getting the data
      // Do whatever you need to do here with the shared data
      this.toggleSidenave(); // Call the function that toggles the Sidenav
    })
  }
}

And we're done!

Now what makes BehaviorSubject special is that it can be initialized with an initial value (To indicate that sidenav is closed by default for example).

You can learn more about BehaviorSubject here

Hope this helped & Happy coding!

Mohamed Karkotly
  • 1,364
  • 3
  • 13
  • 26