1

I have a custom angular component, to which is added an HTML element, dynamically, by a third-party library that I'm using inside the angular component. The particular third-party library is irrelevant, because I'm looking for a solution that will work with a variety of third-party libraries.

I would like to apply/add to these dynamically added HTML elements angular's emulated encapsulation attributes (e.g. _ngcontent-c1). I would prefer an official method if one exists (I was unable to find such a method), or something reasonably reliable otherwise.

Trevor
  • 13,085
  • 13
  • 76
  • 99

1 Answers1

2

Here is an unofficial method I found for doing this:

constructor(private hostRef: ElementRef) { }

getContentAttr(): string {
  const attrs = this.hostRef.nativeElement.attributes
  for (let i = 0, l = attrs.length; i < l; i++) {
    if (attrs[i].name.startsWith('_nghost-c')) {
      return `_ngcontent-c${attrs[i].name.substring(9)}`
    }
  }
}

ngAfterViewInit() {
  // dynamically add HTML element
  dynamicallyAddedHtmlElement.setAttribute(this.getContentAttr(), '')
}

NOTE: This does allow you to style the dynamically added HTML element.

My guess is that the convention for this attribute is not guaranteed to be stable between versions of Angular, so that one might run into problems with this solution when upgrading to a new version (although, updating this solution would likely be trivial in that case).

It would be nice if Angular exposed a function similar to getContentAttr that abstracted away the internal implementation a little.

Trevor
  • 13,085
  • 13
  • 76
  • 99
  • Note: The naming convention is affected by what you put for appId. ` BrowserModule.withServerTransition({ appId: 'xxxxx' }),` – Simon_Weaver Jun 30 '21 at 23:20
  • I have actually used this technique in order to get dynamically generated markdown => html to work with styles without a wrapper element but it's definitely a hack. I'd favor `::ng-deep` over this for sure if that's an option for the situation at hand. – Simon_Weaver Jun 30 '21 at 23:21
  • A better way to get the attribute may be to dynamically create a div and then just take the single attribute from it `const div = this.renderer.createElement('div'); this.encapsulationAttributeName = div.attributes[0].name;`. This will actually work in the constructor - you don't need to wait for any of the lifecycle events. – Simon_Weaver Jun 30 '21 at 23:23
  • An alternate method can be found in [this answer](https://stackoverflow.com/a/60123609/1009922). – ConnorsFan Aug 29 '22 at 16:28