DEV Community

Michael Muscat
Michael Muscat

Posted on • Edited on

Turn a Directive Into a Fragment

You probably shouldn't do this. It's a bad idea. So anyway here's the code.

@Directive()
export class Fragment {
  nativeElement;
  comment;
  childNodes

  constructor() {
    this.nativeElement = inject(ElementRef).nativeElement;
    this.comment = document.createComment(
      ` ${Object.getPrototypeOf(this).constructor.name} `
    );
  }

  ngAfterViewInit() {
    const { nativeElement, comment } = this;
    const parent = nativeElement.parentElement;
    this.childNodes = Array.from(nativeElement.childNodes)
    parent.insertBefore(comment, nativeElement);
    for (const node of this.childNodes) {
      parent.insertBefore(node, nativeElement);
    }
    nativeElement.remove();
  }

  ngOnDestroy() {
    this.comment.remove();
    for (const node of this.childNodes) {
      node.remove()
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This will replace the host element of a directive with whatever is inside it. It even leaves a nice little comment behind.

To see how it's used let's create a provider that will pass through values from the template.

@Directive({
  selector: 'MyProvider',
})
export class MyProvider extends Fragment {
  @Input() value;
}
Enter fullscreen mode Exit fullscreen mode

We're using TitleCase here to distinguish it from rendered DOM elements. Remember that Angular selectors are case sensitive.

Now we can import the directive and use it in a template.

<MyProvider [value]="name">
  <hello></hello>
</MyProvider>
Enter fullscreen mode Exit fullscreen mode

The hello component can then inject the provider to read its value.

@Component({
  selector: 'hello',
  template: `<h1>Hello {{provider.value}}!</h1>`,
  styles: [`h1 { font-family: Lato; }`],
})
export class HelloComponent {
  provider = inject(MyProvider);
}
Enter fullscreen mode Exit fullscreen mode

See it in action on StackBlitz

When we look at the DOM we can see that the host element is replaced with a HTML comment.

Image description

Is This a Good Idea?

Probably not. All it does is add some sugar to <ng-container> in a not-very efficient way. There are probably other side effects I'm not aware of.

Can you think of a use case?

Top comments (0)