Exploring Angular 17 SSR: How to Determine if Component Output Event is Subscribed

Developing a toolbar component with an action button in Angular 17 SSR. The button is a generic one, and I am attempting to determine if the component output events are being observed to determine which buttons are displayed.

Is the code below valid?

<div class="flex flex-row gap-4 justify-between items-center align-middle p-3 bg-[#FFFFFF]">
    <div *ngIf="toolbarType==1 && this.newEvent.observed">
        <button (click)="addClick($event)" class="btn-primary btn-success btn-sm">
            <i class="icofont-plus"></i>
            <span class="">new</span>
        </button>
    </div>
</div>

In the above code, the condition

toolbarType==1 && this.newEvent.observed
determines whether the new button is displayed based on whether the new event is attached. Is this the correct approach?

import { CommonModule, isPlatformBrowser } from '@angular/common';
import { Component, EventEmitter, Inject, Input, OnInit, Output, PLATFORM_ID } from '@angular/core';
import { ToolbarAction } from '../../model/enumToolbar';


@Component({
  selector: 'erp-toolbar',
  standalone: true,
  imports: [
    CommonModule,

  ],
  templateUrl: './toolbar.component.html',
  styleUrl: './toolbar.component.css'
})
export class ToolbarComponent implements OnInit {


  @Input("toolbar") toolbarType!: ToolbarAction;

  @Output("newevent") newEvent = new EventEmitter<any>();


  constructor() {  }

  ngOnInit(): void {

  }
  addClick = (args: any) => {
    this.newEvent.emit(args);
  }
}

Answer №1

Below, the button won't be visible as explained in the following section. The workaround for this issue is:

We can create a new event using @Input, where we specify the callback function we want to run. If necessary, we can make this input mandatory by using

@Input({ required: true }) clickEvent!: any;

This setup ensures that the button is only displayed when an event is passed and that event is triggered successfully upon clicking.

To run the event in the parent context (this), we use bind like so:

[clickEvent]="test.bind(this)"

import { bootstrapApplication } from '@angular/platform-browser';
import { CommonModule, isPlatformBrowser } from '@angular/common';
import {
  Component,
  EventEmitter,
  Inject,
  Input,
  OnInit,
  Output,
  output,
  PLATFORM_ID,
} from '@angular/core';

@Component({
  selector: 'erp-toolbar',
  standalone: true,
  imports: [CommonModule],
  template: `
  <div class="flex flex-row gap-4 justify-between items-center align-middle p-3 bg-[#FFFFFF]">
    asdf
      <div *ngIf="toolbarType==1 && clickEvent">
          <button (click)="addClick($event)" class="btn-primary btn-success btn-sm">
              <i class="icofont-plus"></i>
              <span class="">new</span>
          </button>
      </div>
  </div>
  `,
})
export class ToolbarComponent implements OnInit {
  @Input('toolbar') toolbarType!: any;
  @Input({ required: true }) clickEvent!: any;

  @Output('newevent') newEvent = new EventEmitter<any>();

  constructor() {
    // this.newEvent.subscribe(); <- shows only when this line is uncommented
  }

  ngOnInit(): void {}
  addClick = (args: any) => {
    this.clickEvent(args);
  };
}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ToolbarComponent],
  template: `
    <erp-toolbar [clickEvent]="test.bind(this)" [toolbar]="1"/>
  `,
})
export class App {
  name = 'Angular';

  test(e: any) {
    alert('asdf');
  }
}

bootstrapApplication(App);

Check out the Stackblitz Demo


The observed flag is only set to true when you manually subscribe to the event emitter in the typescript file.

This behavior is not evident in the provided code, hence the button won't be displayed.

Instead, toggle the button using a flag passed as @Input.

import { bootstrapApplication } from '@angular/platform-browser';
import { CommonModule, isPlatformBrowser } from '@angular/common';
import {
  Component,
  EventEmitter,
  Inject,
  Input,
  OnInit,
  Output,
  output,
  PLATFORM_ID,
} from '@angular/core';

@Component({
  selector: 'erp-toolbar',
  standalone: true,
  imports: [CommonModule],
  template: `
  <div class="flex flex-row gap-4 justify-between items-center align-middle p-3 bg-[#FFFFFF]">
    asdf
      <div *ngIf="toolbarType==1 && this.newEvent.observed">
          <button (click)="addClick($event)" class="btn-primary btn-success btn-sm">
              <i class="icofont-plus"></i>
              <span class="">new</span>
          </button>
      </div>
  </div>
  `,
})
export class ToolbarComponent implements OnInit {
  @Input('toolbar') toolbarType!: any;

  @Output('newevent') newEvent = new EventEmitter<any>();

  constructor() {
    // this.newEvent.subscribe(); <- shows only when this line is uncommented
  }

  ngOnInit(): void {}
  addClick = (args: any) => {
    this.newEvent.emit(args);
  };
}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ToolbarComponent],
  template: `
    <erp-toolbar (newEvent)="test($event)" [toolbar]="1"/>
  `,
})
export class App {
  name = 'Angular';

  test(e: any) {

  }
}

bootstrapApplication(App);

Explore the Stackblitz Demo

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

Receiving 'Module not found' error in Typings on specific machines within the same project. Any suggestions on how to troubleshoot this issue?

I have a project cloned on two separate machines, each running VS2015 with Typings 1.8.6 installed. One machine is running the Enterprise version while the other has the Professional version, although I don't think that should make a difference. Inte ...

Universal function for selecting object properties

I've recently delved into TypeScript coding and have run into a puzzling issue that has me stumped. Take a look at the code snippet below: interface testInterface { a: string; b: number; c?: number; } const testObject: testInterface = { a: & ...

The power of Vue reactivity in action with Typescript classes

Currently, I am working on a Vue application that is using Vue 2.6.10 along with Typescript 3.6.3. In my project, I have defined a Typescript class which contains some standard functions for the application. There is also a plugin in place that assigns an ...

Utilizing Angular 9's inherent Ng directives to validate input components within child elements

In my current setup, I have a text control input component that serves as the input field for my form. This component is reused for various types of input data such as Name, Email, Password, etc. The component has been configured to accept properties like ...

How can input be prevented on keydown within angular6?

Is there a way to disable the input field only when a keydown event occurs, without affecting other input fields? xyz.component.html <input type="text" (keydown)="disableInput($event)"> // <-- Disable if keydown <input type="text" (keydown) ...

Unable to create a loop within the constructor to assign API values

I created an export type shown below: export type Program{ key: string; value: string; } An array of values is returned from the API like this: apival = ["abc", "xyz" ...etc] In my component's constructor, I am implementing the f ...

When running npx ts-lint in a Docker environment, an error occurs stating that the module 'typescript' cannot be found

In the process of setting up a dockerized development environment for a node/typescript API project, I am aiming to have everything run within Docker without the need for node, npm, or modules installed on the host system. The main objective is to isolate ...

How to efficiently eliminate duplicates from a JSON array using Angular2

Struggling with filtering my JSON array in Angular2 after transitioning from Angular 1.x. It used to be so simple using 'unique' in the filter function to remove duplicates. Here is a snippet of the JSON data: {"app":"database_1", "host":"my_h ...

The value of 'this.selectedNodes' does not support iteration and is causing a

I am currently utilizing v-network-graphs to generate graphs in the front end with Vue. I have set up my data like this: data(){ return{ test: test_data, nodes:{}, edges:{}, nextNodeIndex: Number, selectedNodes: ref<st ...

Tips for concealing query parameters that are empty or undefined in Angular 2

I'm currently working with Angular2 (RC2) and the new Router (Alpha.8). Is there a way to prevent a query parameter from being displayed in the URL if it is undefined? For example: this.router.navigate(["/results", this.month, this.day], { ...

Angular is putting the page on ice - all clicks are officially off limits

When making an HTTP request to the backend in my project, I need the ability to sort of "freeze" the screen until the request is complete. Specifically, I want to prevent users from being able to interact with certain elements on the page, such as a butt ...

Error: Observable<any> cannot be converted to type Observable<number> due to a piping issue

What causes the type error to be thrown when using interval(500) in the code snippet below? const source = timer(0, 5000); const example = source.pipe(switchMap(() => interval(500))); const subscribe = example.subscribe(val => console.log(val)); V ...

Issue with login form in IONIC: Form only functions after page is refreshed

Encountering an issue with my Ionic login form where the submit button gets disabled due to invalid form even when it's not, or sometimes displays a console error stating form is invalid along with null inputs. This problem seems to have surfaced afte ...

Creating variables in Typescript

I'm puzzled by the variable declaration within an Angular component and I'd like to understand why we declare it in the following way: export class AppComponent { serverElements = []; newServerName = ''; newServerContent = &apos ...

Difficulty encountered when hosting an Angular and .Net Core application on a virtual machine's localhost

Having trouble running an application from a virtual machine on Azure? I'm facing an issue that I can't seem to solve. The application's front end is Angular and the backend is .NET Core 3.1. I'm using ngrok to tunnel my virtual machine ...

Monitoring the loading progress of multiple files using Three JS

Just starting out with Three JS and I'm on a mission to create a loading screen that displays the progress of assets being loaded for a scene. I have a total of 7 different types of assets, including: 4 GLB files 2 Texture files And 1 Obj file Acco ...

What are some ways to enhance the content within a JWT?

After following this tutorial, I am interested in adding additional information to the token. Specifically, I would like to include an 'accessRights' field that can be used for user authorization in both the backend and Angular. Where should I i ...

React error: The DatePickerProps generic type must have one type argument specified

Date Selection Component: import React from "react" import AdapterDateFns from '@mui/lab/AdapterDateFns'; import { LocalizationProvider } from '@mui/lab'; import { DatePicker, DatePickerProps } from '@mui/lab'; cons ...

Encountering overload error with Vue 3 and Axios integration

Currently utilizing Vue 3, Vite, Axios, and TypeScript. While my function functions properly in development, it throws an error in my IDE and during the build process. get count() { axios({ method: "get", url: "/info/count", h ...

Best practices and distinctions when it comes to typing in TypeScript functions

Do the typings below differ in any way, or are they essentially the same with personal preference? interface ThingA{ myFunc(): number; } interface ThingB{ myFunc: () => number; } ...