Home > front end >  Angular: display content projection with nested children
Angular: display content projection with nested children

Time:05-29

I have a component where I would like to display configurable html in the child component. Child components are not accessible to the client. So I think what I'm looking for is content projection

<Parent>
 <ng-template #editTemplate> //custom 
   <button>Edit Me </button>
 </ng-template>    
 <child1>
     ...
     <child4>
       I would like to show the #editTemplate here
     </child4>
 </child1>
</Parent>

so Inside of child4

I have this HTML

<div>
  <ng-content #editTemplate></ng-content>
</div>

However I can't get the button to display. What am I doing wrong here?

CodePudding user response:

What you have here is an interesting combination of content projection and dependency injection.

<ng-template #editTemplate> is being content-projected into Parent component

<Parent>
 <ng-template #editTemplate> <--- projected into <Parent>
   <button>Edit Me </button>
 </ng-template>
</Parent>

so Parent has a direct reference to it via Content Child, if your Parent component looks something like this:

@Component({
  selector: 'parent',
  template: `
  <h2>Parent here!</h2>

  <ng-content></ng-content> <--- #editTemplate will get projected here

  <child1></child1>
  `,
  styles: [`h1 { font-family: Lato; }`],
})
export class ParentComponent {
  @ContentChild('editTemplate') editTemplate: TemplateRef<any>; // <--- reference to #editTemplate
}

However, child4 is nested several layers deep, so dependency injection is your friend here, otherwise you would need to configure a reference in each layer to complete the chain from Parent down to child4.

Using DI, though, you can inject the Parent component directly into child4, who can then access the editTemplate reference on the Parent component instance and use *ngTemplateOutlet to insert the view from that TemplateRef.

So child4 could look like this:

@Component({
  selector: 'child4',
  template: `
  <h3>Child 4 here!</h3>
  <div>And here's the injected template:</div>

  <ng-container *ngTemplateOutlet="projectedTemplate"></ng-container> <--- use *ngTemplateOutlet to render editTemplate
  `,
  styles: [`h1 { font-family: Lato; }`],
})
export class Child4Component {
  projectedTemplate: TemplateRef<any>;

  constructor(private parentComponent: ParentComponent) {} // <-- inject ParentComponent instance

  ngOnInit() {
    this.projectedTemplate = this.parentComponent.editTemplate; // <-- set *ngTemplateOutlet to editTemplate
  }
}

Here's a StackBlitz showing this approach.

  • Related