angularng-templateangular-content-projection

Angular get reference to element to custom attribute directive inside of a projected template with ngTemplateOutlet


I'm having an issue modifying the css and click event on a custom attribute directive

The DIV in question is inside of a reference to a projected `' and passed into a nested child.

Here is how it works

add the ng-template to the parent.

<parent>
  <ng-template #editCellTemplate>
    <div customUpdateDirective>Update</div>
  </ng-template>
  <Child1>
    <Child2>
      <Child3> RENDER editCellTemplate IN HERE </Child3>
    </Child2>
  </Child1>
</parent>

The parent passes the refence to #editCellTemplate to child3 with an observable

export class Parent {
  // ...

  @ContentChild('editCellTemplate') public editCellTemplate?: TemplateRef<any>;

  // ...parent

  ngAfterViewInit() {
    if (this.editCellTemplate)
      this.editService.updateEditCellTemplate(this.editCellTemplate);
  }
}

Here is the customUpdateDirective:

@Directive({
  selector: '[customUpdateDirective]',
})
export class CustomUpdateDirective {
  constructor(public el: ElementRef) {
    console.log(el); //issue here is el is a reference to the parent component and not the div the attribute is attached to.
  }
}

inside of child3 I have this html to render the reference to the editCellTemplate in the loop:

<Child3>
  <!-- this renders the template in a for loop. Everything displays correctly -->
  <div *ngfor...>
    <ng-container *ngTemplateOutlet="editCellProjectedTemplate"> </ng-container>
  </div>
</Child3>

The issue is when I have a reference to the directive customUpdateDirective, it's a refrence to ParentComponent and not the Div

@Component({
  // ...
  providers: [CustomUpdateDirective],
})
export class Child3 implements OnInit {
  editCellProjectedTemplate!: TemplateRef<any> | null;

  private customUpdateDirective: CustomUpdateDirective;

  onInit() {
    this.editService.editTemplateObservable.subscribe((editTemplate) =>
      this.setEditTemplate(editTemplate)
    );
  }

  setEditTemplate(template: TemplateRef<any>) {
    if (Object.keys(template).length < 1) return;

    //setting the template renders the div inside of editCellProjectedTemplate for Child3
    this.editCellProjectedTemplate = template;

    //the problem starts here
    this.customUpdateDirective.el.nativeElement; //nativeElement is the a reference to the Child3 container and not the div it's instantiated on
  }
}

How can I get a reference to the Element <div customUpdateDirective>Update</div> that the customUpdateDirective is applied to when referencing a template? I want to add a click event and style the css.

Here is a stack blitz I've used DI to pass in the template for the demo instead of an observable. How can I get access to the custom element with the editDirective in the SB example?


Solution

  • In AppModule, register the directive to be available to other components in the app via declarations and exports

    @NgModule({
      imports: [BrowserModule, FormsModule],
      declarations: [
        ...
        Child4Component,
        EditDirective,
      ],
      exports: [EditDirective],
      bootstrap: [AppComponent],
    })
    export class AppModule {}
    

    You can also remove providers: [EditDirective], from the child component