Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Markdown no longer being rendered by viewContainerRef component creation #555

Open
nbarrett opened this issue Nov 18, 2024 · 4 comments
Open

Comments

@nbarrett
Copy link

nbarrett commented Nov 18, 2024

First of all - thanks so much for ngx-markdown @jfcere , I've been using it for years and it's awesome! 🎉

Heres's my issue: I've just upgraded my Angular project to version 18 and have your dependencies set to:

    "ngx-markdown": "18.1.0",
    "marked": "12.0.0",

Everything about the app works perflectly except one aspect where I render a component html programmatically using the mechanism below:

  public generateNotificationHTML(walkNotification: WalkNotification, notificationDirective: NotificationDirective, component: Type<WalkNotificationDetailsComponent>): string {
    const componentAndData = new NotificationComponent<WalkNotificationDetailsComponent>(component);
    const viewContainerRef = notificationDirective.viewContainerRef;
    viewContainerRef.clear();
    const componentType: Type<WalkNotificationDetailsComponent> = componentAndData.component;
    const componentRef = viewContainerRef.createComponent(componentType);
    componentRef.instance.data = walkNotification;
    componentRef.changeDetectorRef.detectChanges();
    const html = componentRef.location.nativeElement.innerHTML;
    this.logger.info("notification html ->", html);
    return html;
  }

I need to do it "synchronously" this way as so that I can send the html off to an email service that's also part of my app. When I do this, any html that should render markdown returns an empty string. For instance here is a template that contains such markdown - for diagnostic I've added both Without markdown and With markdown:

import { Component } from "@angular/core";
import { WalkNotificationDetailsComponent } from "./walk-notification-details.component";

@Component({
  selector: "app-walk-notification-changes",
  template: `
    <h1>Without markdown</h1>
    <table style="cellpadding:10; border:1px solid lightgrey;border-collapse:collapse;width: 100%;border-spacing: 5px;">
      <tr>
        <th width="20%" style="border:1px solid lightgrey; font-weight: bold; padding: 6px">Item changed</th>
        <th width="40%" style="border:1px solid lightgrey; font-weight: bold; padding: 6px">From</th>
        <th width="40%" style="border:1px solid lightgrey; font-weight: bold; padding: 6px">To</th>
      </tr>
      <tr *ngFor="let item of walkDataAudit?.changedItems">
        <td style="border:1px solid lightgrey; padding: 6px">{{ item.fieldName | humanise }}</td>
        <td style="border:1px solid lightgrey; padding: 6px">{{item.previousValue}}</td>
        <td style="border:1px solid lightgrey; padding: 6px">{{item.currentValue}}</td>
      </tr>
    </table>
    <h1>With markdown</h1>
    <table style="cellpadding:10; border:1px solid lightgrey;border-collapse:collapse;width: 100%;border-spacing: 5px;"
           *ngIf="walkDataAudit">
      <tr>
        <th width="20%" style="border:1px solid lightgrey; font-weight: bold; padding: 6px">Item changed</th>
        <th width="40%" style="border:1px solid lightgrey; font-weight: bold; padding: 6px">From</th>
        <th width="40%" style="border:1px solid lightgrey; font-weight: bold; padding: 6px">To</th>
      </tr>
      <tr *ngFor="let item of walkDataAudit.changedItems">
        <td style="border:1px solid lightgrey; padding: 6px">{{ item.fieldName | humanise }}</td>
        <td style="border:1px solid lightgrey; padding: 6px" markdown
            [data]="item.previousValue"></td>
        <td style="border:1px solid lightgrey; padding: 6px" markdown
            [data]="item.currentValue"></td>
      </tr>
    </table>
  `
})
export class WalkNotificationChangesComponent extends WalkNotificationDetailsComponent {
}

An example of the rendered html output from the above is here

image

Any suggesstions please? When I render the above template within my UI, it's fine, so it must be something about how the component is rendered via viewContainerRef.

@jfcere
Copy link
Owner

jfcere commented Nov 18, 2024

Hi @nbarrett

Can you provide a reproduction repository?

@nbarrett
Copy link
Author

Hmm, might be difficult as my codebase is around 30K lines so difficult to isolate just a small example. The repo is publc though and the service that does the render is here. There is also an alternative method I added that passes the raw html containing markdown to MarkdownService.parse() here, but that didn't work either.

@nbarrett
Copy link
Author

I did manage to get my component working by doing this, but it bypasses ngx-markdown which I'd prefer not to do:

import { Component } from "@angular/core";
import { WalkNotificationDetailsComponent } from "./walk-notification-details.component";
import { marked } from "marked";


@Component({
  selector: "app-walk-notification-changes",
  template: `
    <table style="cellpadding:10; border:1px solid lightgrey;border-collapse:collapse;width: 100%;border-spacing: 5px;">
      <tr>
        <th width="20%" style="border:1px solid lightgrey; font-weight: bold; padding: 6px">Item changed</th>
        <th width="40%" style="border:1px solid lightgrey; font-weight: bold; padding: 6px">From</th>
        <th width="40%" style="border:1px solid lightgrey; font-weight: bold; padding: 6px">To</th>
      </tr>
      <tr *ngFor="let item of walkDataAudit?.changedItems">
        <td style="border:1px solid lightgrey; padding: 6px">{{ item.fieldName | humanise }}</td>
          <td style="border:1px solid lightgrey; padding: 6px" [innerHTML]="renderMarked(item.previousValue)"></td>
          <td style="border:1px solid lightgrey; padding: 6px" [innerHTML]="renderMarked(item.currentValue) "></td>
      </tr>
    </table>
  `
})
export class WalkNotificationChangesComponent extends WalkNotificationDetailsComponent {

  renderMarked(currentValue: any) {
    const renderedMarkdown = marked(currentValue.toString() || "");
    this.logger.info("renderMarked: currentValue:", currentValue, "renderedMarkdown:", renderedMarkdown);
    return renderedMarkdown;
  }
}

@jeffreykog
Copy link

I'm experiencing the exact same issues in a project where markdown is also being rendered in a dynamic child component. An interesting addition is that this started to break once i migrated my root component to be standalone. When the root component of my app is still non-standalone using an NgModule it will render fine.

I'm also observing that sometimes it does render, but only one single component using markdown at a time. This is on a news article page. Only the first component loads but every time i trigger some kind of event on the page one additional event renders. So when i click a dropdown button ten times it renders ten components.

Maybe irrelevant information, but it feels like change detection makes this break when the component is loaded dynamically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants