Passing events from a parent component to dynamically created child components in Angular

UPDATE: I've decided to tackle this issue in a different way by retrieving dynamic child component values in the parent component's save() function, following the accepted answer.

I am attempting to create a system where a parent component emits an event or observable that triggers actions in its dynamic child components when they listen for it.

I have discovered that using the @Input() and @Output() decorators with dynamic components is not possible, so...

Here is an overview of my current scenario:

  1. The dynamic child component contains a group of 4 input elements. (completed)
  2. The parent component has an Add button that adds instances of the dynamic child component. This button can be used to add multiple instances of the dynamic child component. (completed)
  3. Each dynamic child component has a Delete button to remove its own instance. (completed)
  4. The parent component possesses a property that is an Array of objects. Each item in the array is an object containing the input values of the dynamic child component. (pending)
  5. The parent component also has a Save button, which when clicked should emit an event to the dynamic child component instances, allowing each one to save their input values as objects. (e.g. 3 instances =>
    [ {a:..., b:..., c:..., d:...}, {...}, {...} ]
    ; 5 instances => 5 array items, and so on). (pending)

I am aiming to emit the parent event from the Save button (#5 above) and have each existing dynamic child component instance listen to the event and execute a .push operation on the parent array (#4 above).

This might not be considered best practice, but I haven't yet devised another method to ensure that the values are saved for the existing dynamic component instances after potentially numerous, random add/remove instance actions have been performed.

Parent Component:

html

...
<button (click)="addDetailRow()" type="button">Add Detail Row</button>
<div #detailContainer></div>
...
<button (click)="save()">Save</button>
...

typescript

...
detailRowArray: Array<Object>;
...
addDetailRow() {
    let comp = this._cfr.resolveComponentFactory(DetailRowComponent);
    let detailRowComp = this.container.createComponent(comp);

    detailRowComp.instance._ref = detailRowComp;
    detailRowComp.instance.detailRowArray = this.detailRowArray;
}
save() {
    // TODO: emit an event/observable here
}
...

Dynamic Child Component:

html

<div><input type="text" name="a" [(ngModel)]="detail_item.a" #a></div>
<div><input type="text" name="b" [(ngModel)]="detail_item.b" #b></div>
<div><input type="text" name="c" [(ngModel)]="detail_item.c" #c></div>
<div>
  <input type="text" name="d" [(ngModel)]="detail_item.d" #d>
  <span>
    <button (click)="removeRow()">Remove Row</button>
  </span>
</div>

typescript

...
detail_item: { [key: string]: any; } = {};
detailRowArray: Array<Object>;
...
removeRow() {
  ...
}
...
// TODO: call this function when the parent emits an event/observable
saveToParentArray() {
     this.detailRowArray.push(this.detail_item);
}

P.S. The codebase utilizes template-driven forms, so using FormArray or similar constructs is not feasible at the moment. Thanks for your attention. (I'm still getting familiar with Angular 2+).

Answer №1

After exploring various options, I decided to take a different approach:

Instead of sending events from the parent to dynamic child component instances, I accessed the existing ViewContainerRef variable and used its API in conjunction with a for loop to retrieve the dynamic component instances. From these instances, I extracted the values of 4 input elements, programmatically constructed the desired object, and added it to the parent's detailRowArray. This resolved the issue at hand.

Below is a simplified version of the code snippet:

save() {
    // Temporary variables for creating an array of arrays
    let parentNodeArray: any[] = [];
    let inputs: any[] = [];
    // Loop through each instance of the dynamic component and store them in temp variable
    for (let i = 0; i < this.container.length; i++) {
      let comp: any;
      comp = this.container.get(i);
      parentNodeArray.push(comp.rootNodes[0]);
    }
    // Retrieve values from the set of 4 input elements per instance using a loop
    // Store them in a temporary array variable (utilizing JavaScript)
    parentNodeArray.forEach((elem: any) => {
      inputs.push( Array.prototype.slice.call( elem.querySelectorAll('input') ).map(node => {
        return node.value; });
      );
    });
}

Answer №2

Approach 1 :

The parent and child components can share a data array as input, allowing both to modify it accordingly.

Approach 2 :

An alternative method (as previously mentioned in the comments) is to have the child component trigger an event to communicate with the parent whenever user input is entered.

Approach 3:

If you still prefer to perform actions within the child component from the parent, you can directly call the child component instance to do so.

detailRowComp[1].instance.saveToParentArray()

However, this approach may lead to redundancy and is generally not considered best practice. It's advisable to choose one of the two methods described above.

Answer №3

Implementing objects in place of number or string variables can streamline the process of updating child components with dynamic values.

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

What is the best way to incorporate additional data into a TypeScript object that is structured as JSON?

I'm exploring ways to add more elements to an object, but I'm uncertain about the process. My attempts to push data into the object have been unsuccessful. people = [{ name: 'robert', year: 1993 }]; //I aim to achieve this peopl ...

Angular 2 - Directive fails to work if not properly imported into its module

Trying to use a directive across multiple modules in Angular can be tricky. If you declare it in a shared module and import that module into other modules, you might encounter errors. It seems like the directive only works when declared directly within the ...

What is the proper way to add an SSL cert to an Angular HTTP API request?

Is there a need to utilize a certificate when making an API call to retrieve an access token? For example, if the method is POST at getAccess.com/getCode, how should we handle this in Postman with a certificate attached? I am currently working on an Angula ...

NPM Package: Accessing resources from within the package

My Current Setup I have recently developed and published an npm package in typescript. This package contains references to font files located within a folder named public/fonts internally. Now, I am in the process of installing this package into an Angul ...

Creating a dynamic multi-series line chart in D3 version 4 that incorporates data points with matching colors to their respective lines

In my attempt to enhance a multi-series line chart with D3 V4 in Angular-cli, I have encountered a challenge. Below is the code snippet of what I have been working on. var lookBookData = z.domain().map(function (name) { return { name: name, ...

What steps should be taken to find a particular route when a user logs in for the first time?

I am currently working on an application using Angular 2+, Node.js, and Express.js that includes registration and login authentication functionality. How can I direct first-time users to a specific route upon login, but for all subsequent logins have them ...

Using TypeScript for abstract methods and polymorphism

What do I need to fix in order for this code to function properly? abstract class Animal { // No constructor ... public abstract me():Animal; } class Cat extends Animal { constructor() { super(); } // Why does this no ...

Is it possible for Angular templates to be dynamic?

In my component, I have a variable named "myVar" that determines which ng-template should be displayed. Let's consider the following example of a component template: <div *ngIf="myVar; then myVar; else nothing"></div> <ng-template #foo ...

Error in Angular 2: Component unable to locate imported module

I'm facing an issue where a module I want to use in my application cannot be found. The error message I receive is: GET http://product-admin.dev/node_modules/angular2-toaster/ 404 (Not Found) The module was installed via NPM and its Github reposito ...

Emotion, material-ui, and typescript may lead to excessively deep type instantiation that could potentially be infinite

I encountered an issue when styling a component imported from the Material-UI library using the styled API (@emotion/styled). Error:(19, 5) TS2589: Type instantiation is excessively deep and possibly infinite. Despite attempting to downgrade to typescript ...

Deactivating a form field depending on a selected radio button in Angular 2

If I have two radio buttons, with a click function called localClick for the first button to give value 1 and the second button to give value 2. <div class="ui-g-12"><p-radioButton name="group1" value="Local" (click)=localClick(1) label="Local"&g ...

Show the alias of a type in Vscode Typescript instead of its definition

Here is some code that I am working with: type Opaque<T,U> = T & {_:U}; type EKey = Opaque<number,'EKey'>; type AKey = Opaque<EKey,'AKey'>; type PKey = Opaque<AKey,'PKey'>; let a = <PKey>1; ...

What is the best strategy for managing a sizable react application using react-query?

Since diving into React with functional components and react-query, I've been facing some confusion on how to properly organize my components. Typically, I design components by having a top-level component handle all data access and pass data down to ...

Using Angular: A guide to setting individual values for select dropdowns with form controls

I am working on a project that involves organizing food items into categories. Each item has a corresponding table entry, with a field indicating which category it belongs to. The category is represented by a Guid but displayed in a user-friendly format. C ...

Unlock the potential of Power BI with this step-by-step guide on enhancing the Circle Card visual by incorporating unique formatting

Power BI Tutorial: Adding Formatting Options to the Circle Card Visual After completing step 8, I copied the code into my VS Code and encountered 2 error messages: Error message: "import VisualSettings - Module '"./settings"' has no e ...

Guide to Utilizing the Dracula Graph Library in Angular

Recently, I stumbled upon a JavaScript library that seems to be an ideal fit for my project. The library can be found at: After installing the necessary libraries using npm - npm i raphael graphdracula - new folders were created in node_modules and th ...

Using alternate variables in the watchQuery() function in Apollo Angular will generate the cached data

Currently, I am implementing a feature in my project that allows users to access and analyze data based on various parameters such as year, location, and gender. Below is the code snippet that I have developed for this feature: this._querySubscription = ...

How to dynamically add an HTML element following a specific div class in Typescript and Angular

Is there a way to dynamically insert a component into a div after a specific element with a designated class name using TypeScript? <div class ="{{order.orderId}}"> <div class="enter-here"></div> <other html elements here> < ...

Having trouble incorporating Duo Web SDK into angular application

We are currently working on incorporating Duo Two-factor authentication into our Angular application. For instructions, you can take a look at the documentation available here. The issue we are encountering is that their JavaScript file searches for an i ...

Enhanced Autocomplete Feature with Select All Option in MUI

Currently, I am utilizing Material UI (5) and the Autocomplete component with the option for multiselect enabled. In addition, I am implementing the "checkbox" customization as per the MUI documentation. To enhance this further, I am attempting to incorpor ...