javascripthtmlangularng-templatengtemplateoutlet

How do you hydrate a parent template with child data (Angular 11)


So I have two Angular Components, a parent & a child. I want to do the following:

  1. Define an ng-template in the parent component that references child functions/variables
  2. Pass that template as a parameter to the child component, and
  3. Have the child component display this template using its own instance data.

App.Component (Parent)

import { Component } from "@angular/core";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {}
<reusable-component [customTemplate]="parentTemplate"></reusable-component>

<ng-template #parentTemplate>
  <p>Current color is {{currentColor}}</p>

  <button (click)="changeColorToRed()">
    CHANGE BACKGROUND COLOR TO RED
  </button>

  <button (click)="changeColorToGreen()">
    CHANGE BACKGROUND COLOR TO GREEN
  </button>
</ng-template>

Child Component (reusable-component)

import { Component, Input, OnInit, TemplateRef } from "@angular/core";

@Component({
  selector: "reusable-component",
  templateUrl: "./reusable-component.component.html",
  styleUrls: ["./reusable-component.component.css"]
})
export class ReusableComponentComponent implements OnInit {
  @Input() public customTemplate!: TemplateRef<HTMLElement>;
  currentColor = "white";

  constructor() {}

  ngOnInit() {}

  changeColorToRed() {
    const red = "#FF0000";
    document.body.style.background = red;
    this.currentColor = "red";
  }
  changeColorToGreen() {
    const green = "#00FF00";
    document.body.style.background = green;
    this.currentColor = "green";
  }
}
<ng-container [ngTemplateOutlet]="customTemplate || defaultTemplate">
</ng-container>

<ng-template #defaultTemplate>
  Hello, zuko here!
</ng-template>

How do I provide my parent template with the functions/instance variables from that child Component?

Here's a Stackblitz with the whole project


Solution

  • Most of the things are fine. For passing data...

    Let us first start defining the data to be passed in the Child Component

    Child component TS

    currentColor = "white";
    constructor() {}
    
    ngOnInit() {}
    
    changeColorToRed() {
      const red = "#FF0000";
      document.body.style.background = red;
      this.currentColor = "red";
    }
    changeColorToGreen() {
      const green = "#00FF00";
      document.body.style.background = green;
      this.currentColor = "green";
    }
    
    data = { currentColor: this.currentColor, changeColorToRed: this.changeColorToRed, changeColorToGreen: this.changeColorToGreen };
    

    Now, we pass the context containing the data to the template. Use *ngTemplateOutlet instead of [ngTemplateOutlet] to support chaining

    Child component html

    <ng-container *ngTemplateOutlet="customTemplate || defaultTemplate; context: data">
    </ng-container>
    

    Now, we use the let- attribute to receive the parameters in the parent

    Parent component html

    <reusable-component [customTemplate]="parentTemplate"></reusable-component>
    
    <ng-template #parentTemplate let-currentColor="currentColor" let-changeColorToRed="changeColorToRed" let-changeColorToGreen="changeColorToGreen">
      <p>Current color is {{currentColor}}</p>
    
      <button (click)="changeColorToRed()">
        CHANGE BACKGROUND COLOR TO RED
      </button>
    
      <button (click)="changeColorToGreen()">
        CHANGE BACKGROUND COLOR TO GREEN
      </button>
    </ng-template>
    

    Stackblitz