Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

InnerHTML has no property data when using viewContainerRef.createComponent() and then adding data

Problem - I'm passing a ViewContainerRef into my constructor to instantiate a component named 'MemberCardComponent'. Then I'm trying to fill it with input data, but no data is being populated into the innerHTML.

Stackblitz - I've setup this SB demonstrate my issue: Stackblitz link

const component = this.viewContainerRef.createComponent(MemberCardComponent);
component.instance.MemberCard = memberData; // memberData fetched from Db
const memberCardHtml = component.location.nativeElement.innerHTML; // innterHTML doesn't have MemberCard data.

MemberCard is an input property that looks like this:

@Input() set MemberCard(member: IMemberCard) {
    this.member = member;
}

I see the inner html isn't filled with any of the data. All the fields are empty, where there should be member data. Ex. photoUrl, name, etc

QUESTION - Is there some other action I need to perform to get my input data into the innerHTML?

Here is what I see when I hover over the innerHtml. enter image description here

'<div _ngcontent-yft-c87="" class="card"><div _ngcontent-yft-c87="" class="row g-0 row-eq-height"><div _ngcontent-yft-c87="" class="col-md-4"><picture _ngcontent-yft-c87=""><source _ngcontent-yft-c87="" media="(min-width: 0px) and (max-width: 350px)"><source _ngcontent-yft-c87="" media="(min-width: 351px) and (max-width: 720px)"><source _ngcontent-yft-c87="" media="(min-width: 721px) and (max-width: 1001px)"><source _ngcontent-yft-c87="" media="(min-width: 1002px)"><img _ngcontent-yft-c87="" alt…"float-end"></span></div><div _ngcontent-yft-c87="" class="col-12"><span _ngcontent-yft-c87="">Experience</span><span _ngcontent-yft-c87="" class="float-end"></span></div><div _ngcontent-yft-c87="" class="col-12"><span _ngcontent-yft-c87=""></span><span _ngcontent-yft-c87="" class="float-end"></span></div><div _ngcontent-yft-c87="" class="col-12"><span _ngcontent-yft-c87="">Education</span><span _ngcontent-yft-c87="" class="float-end"></span></div></div></div></div></div><!--container--></div>'

FYI - I'm trying to create the string representation of the component to populate a swiper.js slide. Here is another SO post I created that explains why I need the string representation of my component.

like image 498
user1186050 Avatar asked Jul 07 '26 14:07

user1186050


1 Answers

TL;DR: Add a template so the component can be evaluated and rendered.

Your approach of using innerHTML is not recommended as createComponent is meant to be used together with a template.

To fulfil the expected result I've fixed your Stackblitz example, and it shows the expected behaviour using both approaches, however the innerHTML way needs to be used with a wrapper so the TestComponent changeDetector has a chance to render variable values.

The main changes I did are the following:

  1. Added a custom template so the TestCompnent values can be evaluated
@ViewChild('customTemplate', { read: ViewContainerRef, static: true })
customTemplate: ViewContainerRef;
  1. Added the ViewContainerRef content to the template so it gets rendered, otherwise there's no way to get its html
this.customTemplate.insert(this.viewContainerRef.get(0))
  1. Added a detectChanges which evaluates both detection changes from the parent and its child
detectChanges(): void {
  if (this.customComponent?.changeDetectorRef) {
    this.customComponent.changeDetectorRef.detectChanges();
  } else {
    if (this.customComponent) {
      console.log('Custom component not ready.');
    }
  }
  this.cdr.detectChanges();
}

There's more ways that your desired result can be done, I answered with this one because your code was more suitable to be adapted to it.


Also

For the not recommended solution of using innerHTML the following changes were necessary:

  1. Everything that was mentioned in the previous solution, as there's no other way (known by me) to render and evaluate components.
  2. A wrapper DOM, so you can extract its evaluated innerHTML
  3. A domSatinizer so the DOM can be rendered, direct html code injection is "forbidden" in Angular, and you have to explicitly tell that you're willing to take the risks of using it.
  4. Changing textHtml value type from string to SafeHtml so its accepts sanitized values
  5. For this to work, you'll need to hide the wrapper setting style="display: none"
  6. You can then clean/destroy or do whatever you want with the wrapper as you already got its HTML.
this.testHtml = this.sanitizer.sanitize(
  SecurityContext.HTML,
  this.sanitizer.bypassSecurityTrustHtml(
    document.getElementById('wrapper').innerHTML
  )
);
like image 114
luiscla27 Avatar answered Jul 10 '26 04:07

luiscla27