0

The problem is like this. There is an API that generates PDFs using HTML to PDF mechanism. The API returns the nicely generated PDFs to an Angular app. So far so good. Now there is a new requirement that pieces from these PDFs should be embedded into some Angular components. I get these pieces from the API as strings and now I must find a way to make them run. The problem is that some have JavaScript in them and this code is not executed by the Angular when I bind them to the components. Any advice or workaround is welcome. I have created an example with such code for a easy testing and understanding of the problem.

https://stackblitz.com/edit/angular-fgcvb2?file=src%2Fapp%2Fapp.component.ts

5
  • 2
    Create an iframe and set the source to the content from the PDF? Commented Oct 22, 2023 at 20:38
  • Without getting too deep, Angular's not going to load (dynamic) scripts where you're trying to load them. This will parse out the scripts from your data, and (maybe) get you on the right track, though it looks like you'll need to do some further script debugging after getting them extracted, which is why I'm leaving a comment vs an answer. let scriptDiv = document.createElement('div'); scriptDiv.innerHTML = myHtml ; const scripts = scriptDiv.querySelectorAll('script'); for (let i = 0; i < scripts.length; i++) { document.body.appendChild(scripts[i]); } Commented Oct 24, 2023 at 0:18
  • You could probably also use CefSharp to render this html/js on the backend. But that's most likely pretty expensive to run Commented Oct 24, 2023 at 20:00
  • @K J - U didn't read my context at all. All u say is right but my case is different. On the server side the PDF is build from ASP MVC components and it generates the pdf from HTML to PDF and now those small HTML pieces are pushed to the front angular site. Commented Oct 25, 2023 at 5:31
  • @DA, you overwrote your stackblitz example with something completely different, I've edited your question to use the original one you've posted (my fork). Also, I've added a fixed working example of it in my answer. Commented Oct 25, 2023 at 16:32

1 Answer 1

4
+300

TLDR: Check this fixed working example of the code you provided. This one uses your current approach, so it executes the script tag manually.


The answer is that there's no pretty way of doing what you want, as briefly stated in specs 4.3.1, 8.4 and 8.2.3.5 of the HTML Standard, when parsing <script>, the scripts won't be executed, so you'll have to either do it manually, or renderize your script as a source page:

Changing the src, type, charset, async, and defer attributes dynamically has no direct effect; these attribute are only used at specific times described below.

The scripting flag is set to "enabled" if scripting was enabled for the Document with which the parser is associated when the parser was created, and "disabled" otherwise.

The scripting flag can be enabled even when the parser was originally created for the HTML fragment parsing algorithm, even though script elements don't execute in that case.


1. Execute scripts manually (implemented here)

For your current approach, as it is to call the API's as a service, and then adding the HTML and expecting the JS to be executed. Unfortunately, there's no pretty way of doing this. Similar questions had been asked several times in SO (answer1, answer2, answer3, answer4, etc.). Most of them end up doing something like this:

// (1) to load js files
var scripts = document.getElementsByTagName('script');
Array.from(scripts).forEach(function(node) {
  var parent=node.parentElement
  var script = document.createElement('script');
  script.src = node.src;
  parent.insertBefore(script, node);
  parent.removeChild(node);
});
// (2) to execute directly js
var scripts = document.getElementsByTagName('script');
Array.from(scripts).forEach(function(script) {
    eval(script.innerHTML);
});

As caveant, this solution may end up in other issues (like unsafe-eval, and more).


2. Load your API inside an IFRAME

Best way would be as suggested in comments, to load your whole api response as an individual HTML page inside an IFRAME. Something similar to this:

<iframe id="pdf1"></iframe>
<script> 
    document.getElementById('pdf1').src = '../path/to/api/which/returns/the/html-with-js-here?parameter1=value1&parameter2=value2&etc=values';
</script>

I personally recommend this approach as it is the more natural way. Also, you have something all those other question/answer don't have; you have an API dedicated to retrieve your HTML with JS. So just, instead of calling in it as an API, navigate to it.

Sign up to request clarification or add additional context in comments.

1 Comment

it is a reasonable workaround, not the best solution I've hopped for but at least there is one :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.