Tips for capturing the output of a dynamically rendered component in Angular 8

I need to capture the output from a rendered component using ViewChild. The content of ViewChild is displayed after an ngIf condition is met.

Here is the template code:

<div *ngIf="isModalVisible" class="modal" tabindex="-1" role="dialog">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <!-- ... -->
      </div>
      <div class="modal-body">
        <ng-template #dialogHost (saveModel)="setModel($event)"></ng-template>
      </div>
    </div>
  </div>
</div>

This is the TypeScript file:

import { ChangeDetectorRef, Component, ComponentFactoryResolver, Input, ViewChild, ViewContainerRef } from '@angular/core';
import { DialogContentItem } from 'src/app/models/dialog/content/dialog-content-item';

@Component({
  selector: 'app-dialog-preparator',
  templateUrl: './dialog-preparator.component.html',
  styleUrls: ['./dialog-preparator.component.css']
})
export class DialogPreparatorComponent {
  isModalVisible = false;
  @Input() dialogContent: DialogContentItem;
  @ViewChild('dialogHost', {static: false, read: ViewContainerRef}) dialogHost: ViewContainerRef;

  showModal() {
    this.isModalVisible = true;
    this.changeDetector.detectChanges();
    this.renderComponent();
  }

  renderComponent() {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.dialogContent.component);

    this.dialogHost.clear();

    const componentRef = this.dialogHost.createComponent(componentFactory);
    (componentRef.instance as DialogContentInterface).data = this.dialogContent.data;
  }

  setModel(model: any) {
    // Need to emit the model
  }
}

This is the ProcutCreateEditComponent:

<app-dialog-preparator [dialogContent]="userDialogContent"></app-dialog-preparator>

The TypeScript code for this component:

import { Component } from '@angular/core';
import { UserCreateEditComponent } from 'src/app/components/user/user-create-edit.component';

@Component({
  selector: 'app-product-create-edit',
  templateUrl: './product-create-edit.component.html',
  styleUrls: ['./product-create-edit.component.scss']
})
export class ProductCreateEditComponent {
  userDialogContent: DialogContentItem = new DialogContentItem(UserCreateEditComponent, {});

  constructor() {}

  // ...
}

And here is the UserCreateEditComponent's TypeScript code:

import { Component, Output } from '@angular/core';

@Component({
  selector: 'app-user-create-edit',
  templateUrl: './user-create-edit.component.html',
  styleUrls: ['./user-create-edit.component.scss']
})
export class UserCreateEditComponent {
  @Output() saveModel: EventEmitter<T> = new EventEmitter();
  user = new User();

  constructor() {}

  save() {
    this.saveModel.emit(this.user);
  }
}

In the ProcutCreateEditComponent, I instantiate the UserCreateEditComponent and pass it to the DialogPreparatorComponent. The UserCreateEditComponent shows up in a dialog within the DialogPreparatorComponent. When I click on the save button in the UserCreateEditComponent, the saveModel event should be emitted to the DialogPreparatorComponent. However, I'm encountering the following error message:

Event binding saveModel not emitted by any directive on an embedded template.
Make sure that the event name is spelled correctly and all directives are
listed in the "@NgModule.declarations". ("
      </div>
      <div class="modal-body">
        <ng-template #dialogHost [ERROR ->](saveModel)="setModel($event)"></ng-template>
      </div>
    </div>

It seems the ng-template does not have the saveModel output defined. How can I capture the output from the UserCreateEditComponent at this point?

Answer №1

When creating the component using code, it's necessary to add an event handler in the component code itself rather than in the template. This can be achieved by modifying the renderComponent method to include the subscription.


renderComponent() {
  const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.dialogContent.component);

  this.dialogHost.clear();

  const componentRef = this.dialogHost.createComponent(componentFactory);
  (componentRef.instance as DialogContentInterface).data = this.dialogContent.data;
  (componentRef.instance as DialogContentInterface).saveModel.subscribe((model) => this.setModel(model));
}

It's important to ensure that you unsubscribe when the dynamic component is destroyed to prevent memory leaks from lingering subscriptions.

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

The "my-app" selector in Angular2 did not find any elements upon initial loading

Has anyone encountered this error upon the initial page load in their Angular2 app? It disappears upon navigating through the app, but sometimes reappears if I refresh with CTRL+F5. The routing structure of my app is as follows: AppComponent.ts LoginCom ...

Having trouble reading properties of undefined (specifically 'listen') during testing with Jest, Supertest, Express, Typescript

Issue: While running jest and supertest, I encounter an error before it even gets to the tests I have defined. The server works fine when using the start script, and the app is clearly defined. However, when running the test script, the app becomes undefi ...

Learn how to store the outcomes of an HTTP operation within array.map() in JavaScript

Having read numerous articles, I am a complete beginner when it comes to async programming and struggling to grasp its concepts. My goal is to map a filtered array of objects and return the result of a function (an amount) to set as the value of pmtdue. De ...

Enhancing keyboard accessibility with the spacebar for radio buttons and check boxes in Angular 2

I am currently working on a form in Angular 2 that includes radio buttons and checkboxes. When tabbing through the fields, they are highlighted properly. However, I am facing an issue with the radio buttons - I want them to be selected when I hit the space ...

How do I disable split panel on Ionic 2 login page exclusively?

I have successfully implemented the split-pane feature in my app.html file. However, I am facing an issue where the split pane is being applied to every page such as login and SignUp. Can someone please guide me on how to restrict the split pane function ...

What is the process for generating a fresh PSBT transaction using bitcoinjs-lib?

This is the code I've been working on import * as bitcoin from 'bitcoinjs-lib' const NETWORK = bitcoin.networks.regtest; const psbt = new bitcoin.Psbt({ network: NETWORK }); function p2shAddress(node: bitcoin.ECPairInterface): string { c ...

Error: Unable to retrieve options using this.getOptions function. This issue is unrelated to Vue, it is occurring within

Required Modules "dependencies": { "express": "^4.17.1", "express-static-gzip": "^2.1.1", "react": "^17.0.2", "react-dom": "^17.0.2", "reac ...

Error occurs when trying to open a modal popup within a component due to changes in expression

In my application, I am facing an issue where I have a parent component passing HTML content to a child common component using @ViewChild(). However, when the Child component loads up a popup, the console throws the following error: "ExpressionChangedAft ...

I'm interested in retrieving just the ID specifically when filtering in Angular

This is the filter I have in my service.components.ts this.pijatService .getById(this.id) .pipe(first()) .subscribe((x: any) => { this.form.patchValue(x); console.log(x); this.form.value.nomor_telepo ...

Deduce the Prop Component Type by Examining the Attribute Type

I am facing an issue with a component that requires a `labels` attribute. <Component defaultValue={FURNITURE.BED} labels={[ { value: FURNITURE.BED, text: 'Bed', }, { value: FURNITURE.COUCH, text: 'C ...

Using Typescript: What is the best way to convert a variable into a specific element of an array?

List of Strings: const myStrings = ["one", "two", "three"]; const newString = "two"; The variable newString is currently just a string, but I would like its type to be an element of myStrings. Is there a way to achi ...

Format Currency Fields with Comma Separated Values in Angular and Material UI

In our Angular 9 project, we are implementing currency input fields that require a standard masked label displaying a comma separated format. When the user enters 56000.55 into the input field, upon blur event, we need to show 56,000.55 as a comma separat ...

Tips for integrating Typescript into a pre-existing Vue 3 project

The contents of my package.json file are as follows: { "name": "view", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve" ...

Preserve data even after refreshing the browser in Angular

Is there a reliable method to preserve my data after refreshing my browser? I've experimented with rxjs behavior subject, but it hasn't worked for me. I prefer not to use local/session storage due to security concerns and best practices, especia ...

Combine Immer and NgRx reducer for improved state management

Upon analyzing redux and ngrx, it appears that immer is the preferred library for creating a copy of the state before storing it. In following the example provided by immer, I implemented the following code in my reducer: on(exampleActions.updateExample ...

The deployment of the Angular application to Azure using Github Actions was unsuccessful due to the lack of a package.json file

When attempting to deploy an Angular(10) WebApp to Azure using Github Actions for CI/CD, I encountered an error indicating that the package.json file could not be found during the execution of the npm install command. Below is my yml file: name: Deploy to ...

`"Unable to execute the 'ng build --env=prod' command"`

I have a JavaScript website that I need to rebuild with some changes I made. In order to build the application, I was instructed to run this command from the source files directory: ng build –env=prod However, when I try to do so, I keep encountering t ...

There is no definition for Angular 4 Material stepper tags

After thoroughly studying the Angular Material stepper documentation, I encountered an issue when attempting to integrate the HTML Material stepper tag into my Angular application. The error message stated that it's not a known element. HTML: <ma ...

@angular/common@~5.1.1 is needed as a peer dependency for @angular/[email protected], however it is not currently installed

There seems to be a peer dependency issue with @angular/common@~5.1.1 while trying to install the angular date picker from NPM console. Upon running the command npm install angular2-material-datepicker, I encounter the above error message. npm install ...

The name is not found when using attribute, but it is found when using extends

Lately, I've encountered difficulties with creating large TypeScript modules, and there's one thing that has been puzzling me. Specifically, the following scenario doesn't seem to work: // file A.ts export = class A { } // file main.ts imp ...