Connect a click event from one component to another component

Can Angular bind a click event dynamically from a component to another existing component on the page? Check out this image for reference.

In my app, I have 4 simple components - a shell component that defines the layout, a header component (blue outline) and a navigation component (red outline) within the shell component. The green component represents the current route-loaded component.

To update the navigation component, I use a basic service setup like this:

@Injectable({ providedIn: 'root' })
export class NavigationService {

  private titleSource = new Subject<string>();
  private actionsSource = new Subject<ActionButton[]>();
  private menuItemsSource = new Subject<MenuItem[]>();

  title = this.titleSource.asObservable();
  actions = this.actionsSource.asObservable();
  menuItems = this.menuItemsSource.asObservable();

  constructor() {
  }

  setTitle(title: string) {
    this.titleSource.next(title);
  }

  setActions(actions: ActionButton[]) {
    this.actionsSource.next(actions);
  }

  setMenuItems(menuItems: MenuItem[]) {
    this.menuItemsSource.next(menuItems);
  }
}

The navigation component simply integrates with this service as follows:

@Component({
  selector: 'app-navigation',
  templateUrl: './navigation.component.html'
})
export class NavigationComponent implements OnInit {
  title: string = "";
  actions: ActionButton[] = [];
  menuItems: MenuItem[] = [];

  constructor(private navigation: NavigationService)  { 
    navigation.title.subscribe((title) => this.title = title);
    navigation.actions.subscribe((actions) => this.actions = actions);
    navigation.menuItems.subscribe((menuItems) => this.menuItems = menuItems);
  }
}

The navigation is displayed using this code snippet:

<div class="navigation">
    <div *ngIf="title.length" class="navigation__title">
        <h1>{{title}}</h1>
    </div>
    <div class="navigation__menu">
        <ul>
            <li *ngFor="let menuItem of menuItems"  [routerLinkActive]="['active']">
                <a routerLink="{{menuItem.path}}">{{menuItem.title}}</a>
            </li>
        </ul>
    </div>
    <div class="navigation__actions">
      <a *ngFor="let actionButton of actions" class="btn btn--primary">{{actionButton.title}}</a>
    </div>
</div>

Next, in the currently active component (the green one), I manage the title and items like this:

 ....

  constructor(private employeeService: EmployeeService, private navigation: NavigationService, private drawerService: NzDrawerService) { 
    navigation.setTitle('');
    navigation.setActions([
      {
        path: '',
        title: 'Add Employee'
      }
    ]);
    navigation.setMenuItems([
      {
        path: '/employees',
        title: 'Employees'
      },
      {
        path: '/teams',
        title: 'Teams'
      }
    ]);
  }
  ....

Additionally, in this same active component, I have defined a method named 'createEmployee':

  createEmployee() {
    const drawer = this.drawerService.create<CreateEmployeeComponent>({
      nzContent: CreateEmployeeComponent,
      nzClosable: false,
      nzWidth: '33%',
    });

    drawer.afterClose.subscribe(data => {
      this.loadEmployees();
    });
  }

Now, my query involves whether it's feasible to invoke this method by clicking the button generated within my navigation component. Moreover, can this be set dynamically from the currently activated component similar to how we handle the title and navigation items?

Answer №1

To achieve this, utilize the concept of Object orientation

Begin by creating a base-class

export class EmployeeCreateClass {
 createEmployee() {
    const drawer = this.drawerService.create<CreateEmployeeComponent>({
      nzContent: CreateEmployeeComponent,
      nzClosable: false,
      nzWidth: '33%',
    });

    drawer.afterClose.subscribe(data => {
      this.loadEmployees();
    });
  }
}

Extend it with Navigation:

export class NavigationComponent extends EmployeeCreateClass implements OnInit {
...

The navigation HTML:

<button (click)="createEmployee()"></button>

Repeat the process for components:

export class CurrentComponent extends EmployeeCreateClass implements OnInit {
....

The HTML:

<button (click)="createEmployee()"></button>

======================================================== Apologies for misunderstanding earlier, consider refining further with something like:

@Injectable({ providedIn: 'root' })
export class NavigationService {

  private titleSource = new Subject<string>();
  private actionsSource = new Subject<ActionButton[]>();
  private menuItemsSource = new Subject<MenuItem[]>();
  private actionItems = [];

  title = this.titleSource.asObservable();
  actions = this.actionsSource.asObservable();
  menuItems = this.menuItemsSource.asObservable();

  constructor() {
  }

  setTitle(title: string) {
    this.titleSource.next(title);
  }

  setActions(actions: ActionButton[]) {
   this.actionItems = [];
    for(let i = 0; i < actions.length; i++) {
      this.actionItems.push(new Subject<void>());
    }
    this.actionsSource.next(actions);
  }

  setMenuItems(menuItems: MenuItem[]) {
    this.menuItemsSource.next(menuItems);
  }
}

In the HTML of navigation:

      <a *ngFor="let actionButton of actions; let i = index" class="btn btn--primary" (click)="actionButtonClicked(i)">{{actionButton.title}}</a>

In the TS of the navigation:

  actionButtonClicked(i: number) {
    this.navigation.actionItems[i].next();
  }

In your component:

navigation.setActions([
      {
        path: '',
        title: 'Add Employee'
      }
    ]);
// the bottom should get triggered every time actionButton gets `next`ed.
navigation.actionItems[0].subscribe(_ => { this.createEmployees(); });

Note that this is a quick attempt without an IDE, but hopefully it provides some assistance.

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

Error occurs when Component is used in ngFor

Attempting to implement a component in ngFor as suggested in this Stack Overflow thread results in an error message (Expression has changed after it was checked. Previous value: 'undefined'. Current value: 'false'.). The error occurs w ...

Is it possible in Typescript to pass method signature with parameters as an argument to another method?

I am working on a React app where I have separated the actions into a different file from the service methods hoplite.actions.ts export const fetchBattleResult = createAsyncThunk<Result>( 'battle/fetchBattleResult', HopliteService.battleRe ...

Creating adaptable Object Properties using Zod

Just dipping my toes into Typescript, Zod, and Trpc. Let's say I have a schema for animals and plants. I want to keep all their shared properties in the main part of the schema, while putting more specific details into a sub-object named custom. (jus ...

What is the best way to restrict a mapped type in typescript to only allow string keys?

In the Typescript documentation, I learned about creating a mapped type to restrict keys to those of a specific type: type OptionsFlags<Type> = { [K in keyof Type]: boolean; }; If I want to use a generic type that only accepts strings as values: t ...

flushMicrotasks does not function properly in conjunction with the image.onload event

Working on an Angular project, I'm currently developing an object with an image field. The method responsible for loading the image returns a promise that resolves in the onload function of the image. When trying to test this method using the flushMi ...

Angular TypeScript test checking file size with Jasmine

Seeking optimal method for testing File size during input type="file" change event. Currently, my test specification appears as follows: it('attach file with too large size', () => { const file: File = { name: 'filename', ...

Converting image bytes to base64 in React Native: A step-by-step guide

When requesting the product image from the backend, I want to show it to the user. The issue is: the API response contains a PNG image if the product has an image, but returns a (204 NO Content) if the product does not have an image. So, I need to display ...

Experiencing problems with the Locale setting when utilizing the formatNumber function in Angular's core functionalities

I am having trouble formatting a number in Angular using the formatNumber function from the Angular documentation. Here is my code snippet: import {formatNumber} from '@angular/common'; var testNumber = 123456.23; var x = formatNumber(Numb ...

Is it possible to utilize Typescript and Browserify in tandem?

As I explore the compatibility of TypeScript and Browserify, one perplexing aspect stands out - they both utilize 'require' but for different purposes. TypeScript uses 'require' to import other TS modules, while Browserify employs it to ...

Displaying properties of a class in Typescript using a default getter: Simplified guide

Here is an interface and a class that I am working with: export interface ISample { propA: string; propB: string; } export class Sample { private props = {} as ISample; public get propA(): string { return this.props.propA; } public se ...

Mastering regular expressions in TypeScript

My goal is to perform linting on staged files that are either .ts or .tsx and located within the src folder. I am aware that for selecting all js files one can use "*.js": [--list of commands--] inside the lint staged property. I'm curious to learn m ...

Ways to retrieve the most recent value from an Angular 4 subscription

What is the best way to retrieve the most recent value from a subscribe method in Angular 4? this.AbcSrvice.value.subscribe(data => {console.log(data)}) ...

Can you explain the process of the assignment part in the code line of Angular2?

I’ve been delving into the angular2-rxjs-chat project on GitHub to enhance my knowledge. Within the code linked here, there is a specific line of code that caught my attention: threads[message.thread.id] = threads[message.thread.id] || message.thread; ...

Unable to make changes to the document

Having trouble updating a document by ID using mongoose and typescript. I'm testing the api and passing the id as a parameter. I've experimented with different methods of updating by ID, but for some reason, it's not working. Can update by ...

Tips for formatting dates in Angular 6

I am currently working on a function that displays real-time dates based on user input. Currently, when the user enters the input, it is displayed in the front end as follows: 28.10.2018 10:09 However, I would like the date to change dynamically based on ...

Convert the XML response from an API into JSON

Is there a way to convert my XML response into JSON using Angular? This is the response I am working with: <?xml version="1.0" encoding="utf-8"?> <string xmlns="http://tempuri.org/"><?xml version="1.0" encoding="utf-8"?&gt; &lt;Fer ...

Is it possible to open a PDF file in a new tab using Angular 4?

When attempting to open a PDF file using the window.open() function, I encountered an issue where the window would open and close automatically, causing the file to be downloaded rather than displayed in a new tab. Despite not having any ad blockers inst ...

The default value for the logged in user in Angular 7 is set to null when

In my login component, I have a form where users can enter their credentials and submit for authentication using the following function: this.auth.login(this.f.email.value, this.f.password.value) .pipe(first()) .subscribe( data ...

Circular Dependency Detected in Angular 7 Library: A Tight Web of Directive, Service, and Module Interconnections

After setting up a new Angular 7 project with a library that includes a directive, a service, and a module (where the directive gets the service injected and the service has an injectionToken exported in the module), I encountered these warnings during com ...

Using TypeScript with Next.js getStaticProps causes errors

Currently, I am grappling with utilizing getStaticProps along with TypeScript. Initially, I attempted to achieve this using a function declaration: import { Movie } from './movies/movie' import { GetStaticProps } from 'next' export asy ...