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

Customize nestjs/crud response

For this particular project, I am utilizing the Nest framework along with the nestjs/crud library. Unfortunately, I have encountered an issue where I am unable to override the createOneBase function in order to return a personalized response for a person e ...

Encountering a host configuration issue while trying to use next/image in a TypeScript environment

I understand that when using Next.js image components without TypeScript, the URL must be configured in next.config.js, but I'm unsure why this doesn't work with TypeScript. ..., is not set up under images in your next.config.js. Learn more her ...

Creating a mat-tree component in Angular Material 6.0.1: Step-by-step guide

I am encountering an issue with my material angular mat-tree based application. The code runs without errors, but the values are not displayed on the screen. I need help resolving this problem so that I can proceed with the development. Upon opening the a ...

Eliminate a specific choice from a drop-down menu in an Angular application

I am implementing a feature where clicking on a button adds more select drop downs. I want to ensure that the selected options in these new dropdowns do not duplicate any already chosen options. Below is the code snippet used for the select drop down: < ...

Display the modal in Angular 8 only after receiving the response from submitting the form

I am encountering an issue where a pop-up is being displayed immediately upon clicking the submit button in Angular 8, before receiving a response. I would like the modal to only appear after obtaining the response. Can someone assist me with achieving thi ...

What is the best way to showcase a half star rating in my custom angular star rating example?

component.ts import { Component } from '@angular/core'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { projectRating ...

Tips for defining the types of nested arrays in useState

Looking for guidance on how to define types for nested arrays in a useState array This is my defined interface: interface ToyProps { car: string | null; doll: number | null; } interface SettingsProps { [key: string]: ToyProps[]; } Here is the stat ...

Instantiate the component array upon object instantiation

I'm currently in the process of learning Angular 2, so please bear with me if this question seems trivial. I am attempting to create a dynamic form that can be bound to a model. However, I am encountering an issue where I am unable to initialize my ar ...

Enhancing Apollo Cache Updates using TypeScript null checks

Currently, I am utilizing apollo codgen to automatically generate types for my graphql queries in TypeScript. However, I have noticed that the generated types contain numerous instances of null values, leading to an abundance of if checks throughout my cod ...

Making an Http Get request in Angular 2 by passing a JSON object

How can I make an HTTP GET request and send a JSON object along with it? Here is the JSON object: {{firstname:"Peter", lastname:"Test"} I want to pass this object in the HTTP request to receive a list of matched persons. Is this possible? The example o ...

Organizing objects into arrays in Node.js

I have an array and I need to concatenate an object after the array. Here is my array: const users = [ {name: "Joe", age: 22}, {name: "Kevin", age: 24}, {name: "Peter", age: 21} ] And here is my object: ...

The data source retrieved through the "get" API method is missing from the mat-table

Recently, I've started working with angularCLI and I'm facing an issue in creating a table where the dataSource is fetched from a fake API. Let me share my component class: import { Component, OnInit } from '@angular/core'; import { Fo ...

Opting out of notifications using Angular's NGXS

I'm new to NGXS in Angular and have recently learned that you don't need to manually unsubscribe when using the async pipe. However, I am currently subscribing to both query parameters and dispatched actions. Do I still need to manually unsubscri ...

I am looking for guidance on removing the bottom line from the ionic 4 segment indicator. Any advice or tips on

.segment-button-indicator { -ms-flex-item-align: end; align-self: flex-end; width: 100%; height: 2px; background-color: var(--indicator-color); opacity: 1; } I am a beginner in hybrid app development and ...

Obtaining Data from a Database Using Angular

I have developed a front-end website using Angular as my framework, integrated with node.js. To interact with the database, I created a "server.ts" file and connected it successfully to my DB. Now, my goal is to fetch data from the database and display i ...

Achieve validation of numerous variables without the need for a string of if-else

If we have three variables, such as firstName, lastName, and email, how can we check if they are empty or not without using multiple if else blocks? let firstName = "John"; let lastName = "Doe"; let email = "john.doe@example.com"; if (firstName.trim() == ...

Guide on integrating ng2-bootstrap with .NET Core

I have been attempting to set up ng2-bootstrap in an Angular2 project using the .NET Core template (more information about the template can be found here). However, I am facing difficulties as the ng2-bootstrap components are not functioning properly even ...

Is it possible to transfer files using web-bluetooth technology?

As I work on developing an embedded system that counts the number of cars, saves their speed and time data in a logs file using rsyslog. Simultaneously, I am creating a web-API (in Typescript/Angular with Electron for Desktop usage and later Web as well) t ...

Leverage the key-value pairs in JSON to automatically suggest types within the function parameters

If we have data structured like this: { "key1": "hardcoded string", "key2": "another hardcoded string", } Imagine a function with 2 parameters where the first parameter should refer to key1 and the second to i ...

The Typescript compiler is unable to ignore imported JavaScript files

Recently, I started working with TypeScript and Angular 2, but encountered a problem that has left me puzzled. Initially, everything was going smoothly with the angular2 quickstart project until I attempted to import a plain JavaScript file. import * as m ...