How can I pass properties from a childComponent to a parent component in Angular 2 without prior knowledge of the childComponent's class?

My main goal is to accomplish the following :

I currently have a component setup like this:

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

@Component({
  selector: 'like',
  template: '<p>this is the like component<p>'      
})

export class LikeComponent implements OnInit{
  title: string = 'Like Component';

  @Output() sendTitle = new EventEmitter();

  ngOnInit() {
    this.sendTitle.emit({title: this.title});
  }    
}

The intention is to pass the title from this component to its parent:

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

@Component({
  moduleId: module.id,
  selector: 'panel',
  template: `
      <div class="panel panel-default">
        <div class="panel-heading">
          <h2 class="panel-title">{{title}}</h2>
        </div>
        <div class="panel-body">
          <ng-content (sendTitle) = "receiveTitle($event)"></ng-content>
        </div>
      </div>
  `
})
export class PanelComponent {
  title = 'default title';

  receiveTitle(arg) {
    this.title = arg.title;
    console.log(arg);
  }
}

This way, I can utilize the PanelComponent for any content I want in a panel:

<panel>
    <like></like>
</panel>

<panel>
    <another-component></another-component>
</panel>

<panel>
    <example-component></example-component>
</panel>

Every time a Panel component is displayed, it will attempt to retrieve the title from the event if present.

It seems that using ng-content for this purpose does not work as expected compared to regular parent/child components. Since I am new to Angular 2, I would love to hear your thoughts!

In summary, my aim is for a parent component to access properties of a child component without being specific (hence the use of <ng-content>).

To clarify, I do not want to achieve this through input like so:

<panel [title] = "'This is the panel title of the like component'">
    <like></like>
</panel>

<panel [title] = "'This is the panel title of the another-component'">
    <another-component></another-component>
</panel>

Answer №1

After extensive research, I came across a solution similar to Sasxa's, but adaptable to any child component. The important point is that the "selector" for @ComponentChild can be a string.

interface TitledComponent {
    title?: string;
}

@Component({
    selector: 'panel',
    template: `<div class="panel">
                    <h2>{{title}}</h2>
                    <ng-content></ng-content>
                </div>`
})
export class PanelComponent {
    title = 'default title';

    @ContentChild('titled') childComponent: TitledComponent;

    ngAfterContentInit() {
        if (this.childComponent)
            this.title = this.childComponent.title || 'default title';
    }
}

Here is an example of how the HTML should be structured:

<panel>
    <like #titled></like>
</panel>
<panel>
    <other #titled></other>
</panel>
<panel>
    <p>I don't have a title</p>
</panel>
<panel>
    <like></like>
    <!-- This won't be selected -->
</panel>

Answer №2

Give this a shot:

create new Component {
  heading = 'custom heading';
  @ContentChild(FavoriteComponent) favorite: FavoriteComponent;

  afterContentLoaded() {
    this.heading = this.favorite.heading;
  }
}

Answer №3

To implement the event approach, utilize the element.dispatchEvent function to generate a bubbling event. Remember that EventEmitter events do not bubble and are best suited for (xxx) binding.

In the child component, this implementation would appear as follows:

  constructor(private elRef:ElementRef, private renderer:Renderer) {}

  onBlur($event) {
    this.renderer.invokeElementMethod(this.elRef.nativeElement, 
        'dispatchEvent', 
        [new CustomEvent('input-blur', { bubbles: true })]);

For further information, you can refer to my related response on how to determine when ANY form input field loses focus in Angular2

Answer №4

It seems like this code snippet should solve the issue in the panel template:

<h2>{{title}}</h2>
<panel class="col-xs-6 col-md-4">
    <like (sendTitle)="receiveTitle($event.title)"></like>
</panel>

Make sure to also add this code in the component file:

receiveTitle(title) {
    this.title = title;
    console.log(event);
}

Remember that you can send Output events similar to how you handle click events. Using () indicates sending data from HTML to the component, while [] indicates binding data from the component to the HTML.

I hope this solution aligns with what you were looking for!

If it's not clear why ng-content is being used, keep in mind that each click will update the parent title based on actions taken in the child component.

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

Typescript compiler not excluding the node_modules directory

{ "compilerOptions": { "target": "es5", "module": "commonjs", "moduleResolution": "node", "sourceMap": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "removeComments": false, "noImplicitAny": false ...

The Cypress tests run successfully on a local machine, but encounter issues when running on Gitlab CI

My current setup involves utilizing Cypress to test my Angular application. Interestingly, everything runs smoothly when I execute the tests locally. However, the scenario changes once I run the tests in Gitlab CI as it ends up failing. Looking at my pack ...

Why did the homepage load faster than the app.component.ts file?

I am facing an issue where the homepage is loading before app.component.ts, causing problems with certain providers not working properly due to import processes not being fully completed. Despite trying to lazy load the homepage, the console.log still sho ...

Server-Side Rendering (SSR) is failing to generate any HTML output

I recently integrated Angular Universal SSR into my Angular application. Everything is working perfectly fine locally without any errors during the yarn build:ssr or yarn dev:ssr commands. Upon starting the server and inspecting the source, I can see all t ...

Using Angular 12 to seamlessly import JSON files into TypeScript

My project contains a JSON file located at src/assets/version.json with the following content: {"VERSION":"1.0.0"} I have imported this file into a TypeScript file (e.g., *.ts) as shown below: import * as VersionInfo from 'src/ass ...

Steps to troubleshoot a simple function that manages asynchronous tasks

Looking to develop a versatile function that can handle async functions, execute them, and catch any errors that may arise. Coming from a javascript background, I initially managed to create a function that did just this. However, my attempt to enhance it ...

Issue with custom validator in Angular 6: setTimeout function not functioning as expected

Currently, I am in the process of following a tutorial to implement Asynchronous validation in Angular. The goal is to create a custom validator named shouldBeUnique that will be triggered after a 2-second delay. To achieve this, I have utilized the setTim ...

receiving null instead of an identifier

Attempting to perform an update operation in Angular. Upon submission after updating, a random number is displayed at the end of the API rather than the specific id number. The request URL appears as follows Request URL: http://localhost:4200/api/auth/rol ...

Tips for creating a unit test case for a specialized validator in angular reactive forms

Looking for guidance on creating a test case for this specific method: export class CustomErrorStateMatcher implements ErrorStatematcher { isErrorState(control: FormControl,form:NgForm | FormGroupDirective | null){ return control && control.inval ...

Sharing data from a Provider to a function in React can be done through various methods

After developing an NPM library that contains various utility functions, including one for calling endpoints, I encountered a roadblock when trying to set the Axios.create instance globally. Initially, my idea was to create a Provider and establish a cont ...

The compatibility between TypeScript and the Node.js crypto module is currently not fully optimized

Incorporating encryption into my project using vuejs and typescript has been a challenge. I managed to implement it in the .vue file successfully, but encountered an issue when trying to write the encryption into a typescript class. The mocha test runs fin ...

The npm module appears to be installed but is not displaying

The module has been successfully installed in my project, however, it is not being detected. How can I resolve this issue? https://i.stack.imgur.com/SjisI.jpg ...

Tips for validating Enum Strings using the newest version of Joi?

Is there a way to validate Enum String? In the past, I followed this advice from: https://github.com/hapijs/joi/issues/1449 enum UserRole { Admin = 'admin', Staff = 'staff' } const validator = { create: Joi.object().keys({ ...

Querying data elements using Graphql mutations: a step-by-step guide

const MUTATION_QUERY = gql` mutation MUTATION_QUERY( $name: bigint! ) { insert_name( objects: { name: $name } ) { returning { id name } } } `; const [onClick, { error, data }] = useMut ...

Merging arrays with the power of ES6 spread operator in Typescript

My goal is to merge two arrays into one using the spread object method as shown in the code snippet below: const queryVariable = { ...this.state, filters: [...Object.keys(extraFilters || {}), ...this.state.filters], } The this.state.filte ...

"Learn the steps for accessing the most recent version of a ReactJS application from the server

I have my react app hosted on a Netlify domain. How can I ensure that users who have previously loaded older versions of the app are redirected to the most recent deployed version? ...

The CSS scale property is not working as expected when used in a React.js application, specifically

working environment ・next.js ・react ・typescript https://www.youtube.com/watch?v=ujlpzTyJp-M A Toolchip was developed based on the referenced video. However, the --scale: 1; property is not being applied. import React, { FunctionComponent ...

Stop the controller from reloading when navigating in Angular2/Ionic2

Initially, I developed my app using tabs. When navigating to a page, the view would load for the first time (fetch data from API and display it), and upon returning to the same page, nothing would reload because the controller did not run again. Recently, ...

Getting the URL segment while using a CanActivate guard

Ensuring users authenticate before accessing a requested page using a guard is crucial. In one specific scenario, a user may enter a URL with query parameters like To retrieve both the parameters and the URL segment within the guard, a piece of code has ...

Issue with displaying ngRx/store Observable values in Angular template

Using examples from the ngRx documentation, I am implementing a redux model in our Angular application. The code functions correctly, with actions firing and updating the store as expected. However, I am facing an issue where the template remains empty, an ...