{"id":94290,"date":"2022-05-23T16:46:04","date_gmt":"2022-05-23T16:46:04","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=94290"},"modified":"2022-05-24T20:40:38","modified_gmt":"2022-05-24T20:40:38","slug":"inline-pdf-viewer-in-an-angular-app-now-you-can","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/angular\/inline-pdf-viewer-in-an-angular-app-now-you-can\/","title":{"rendered":"Inline PDF Viewer in an Angular App? Now you can"},"content":{"rendered":"<p>PDF and web have never been friends &#8212; so much so that most users always download a PDF before viewing it. This has changed a lot in recent years. Browsers do support the viewing of pdfs in separate tabs nowadays. For most use cases, downloading plus the ability to show a PDF in a tab would suffice.<\/p>\n<p>But, developers have been hungry. They wanted to show the PDF inside their website so that users could view and sign them, read them like a book, and so on. In short, a better user experience was lacking when users were forced to download a PDF or open it in a separate tab\/window. This created the need for a PDF viewer which can be easily integrated into Angular. There were a lot of small and partial solutions arising in this space, yet none was available for Angular developers.<\/p>\n<p>Angular developers like myself have suffered greatly due to a lack of a quality library that can be used to show a pdf without losing the user experience.<\/p>\n<p>Enter <a href=\"https:\/\/www.npmjs.com\/package\/ng2-pdfjs-viewer\"><em>ng2-pdfjs-viewer<\/em><\/a>, and the Angular developer&#8217;s fight with PDF is over!<\/p>\n<p>The <em>ng2-pdfjs-viewer<\/em> component is built on top of Mozilla&#8217;s <em>viewerjs<\/em> and <em>pdfjs<\/em>, so its core is solid. It does have easy-to-use attributes along with the ability to customize anything which <em>pdfjs<\/em> supports. You can find the code demonstrated in this article <a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/05\/app.component.zip\">here<\/a>.<\/p>\n<p>Usage can be as simple as<\/p>\n<pre class=\"theme:vs2012 lang:c# decode:true\">&lt;ng2-pdfjs-viewer pdfSrc=\"sample.pdf\"&gt;&lt;\/ng2-pdfjs-viewer&gt;<\/pre>\n<p>And viola, the PDF sits right inside the Angular application like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-94291\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/05\/diagram-description-automatically-generated-1.png\" alt=\"An image showing the ng2-pdfjs-viewer with a sample PDF file\" width=\"1429\" height=\"476\" \/><\/p>\n<p>Now the question is &#8211; what else can I get out of it?<\/p>\n<p>Here are some examples. Suppose you wanted to show two versions of the same document for comparison side by side in your web application. Now you can. Do you want to open the PDF in a separate browser window for traditional viewing? No problem. Do you like to show a print preview dialog automatically after opening the PDF in a new browser window? <a href=\"https:\/\/ng2-pdfjs-viewer.azurewebsites.net\/externalwindow\">Piece of cake<\/a>. Automatically download the pdf? <a href=\"https:\/\/ng2-pdfjs-viewer.azurewebsites.net\/autodownload\">Got it<\/a>.<\/p>\n<p>This pdf viewer supports tons of other features, and you have fine control of what to do with it.<\/p>\n<h2>Setup ng2-pdfjs-viewer in an Angular App<\/h2>\n<p>Follow these steps to set up the component:<\/p>\n<p>1. Installation &#8211; Like any other package &#8211; get it from the npm registry<\/p>\n<pre class=\"theme:vs2012 lang:ps decode:true\">$ npm install ng2-pdfjs-viewer --save<\/pre>\n<p>This is a standard npm package installation command. Make sure it gets installed in <em>dependencies<\/em> using the <code>--save<\/code> parameter.<\/p>\n<p>2. Configuration &#8211; Let your app know you would like to use it<\/p>\n<p>Set it up in your angular <code>AppModule<\/code>:<\/p>\n<pre class=\"lang:c# theme:vs2012\">import { BrowserModule } from '@angular\/platform-browser';\r\nimport { NgModule } from '@angular\/core';\r\nimport { AppComponent } from '.\/app.component';\r\nimport { PdfJsViewerModule } from 'ng2-pdfjs-viewer'; \/\/ &lt;-- Import PdfJsViewerModule module\r\n@NgModule({\r\n  declarations: [\r\n\tAppComponent,\r\n  ],\r\n  imports: [\r\n\tBrowserModule,\r\n\tPdfJsViewerModule \/\/ &lt;-- Add to declarations\r\n  ],\r\n  providers: [],\r\n  bootstrap: [AppComponent]\r\n})\r\nexport class AppModule { }<\/pre>\n<p>As shown here, the module to be imported is <code>PdfJsViewerModule<\/code>; this is required as this is the module that makes sure <em>ng2-pdfjs-viewer<\/em> is ready to be used.<\/p>\n<p>It is equally important to add the module <code>PdfJsViewerModule<\/code> into the <code>imports<\/code> section of <code>@NgModule<\/code>. With this step, you are almost ready to use the inline PDF viewer.<\/p>\n<p>3. Build &#8211; Add a build step, so that your angular app has a copy of pdfjs<\/p>\n<p>The <code>ng2-pdfjs-viewer<\/code> component is built on top of <em>pdfjs<\/em>, which also means that it\u2019s needed for this angular component to work properly. There are several ways this can be achieved, from the manual process of copying relevant files to automated build scripts. Here are the two most popular mechanisms angular developers use when they need extra files to be copied as part of the build step.<\/p>\n<p>Either add an <em>angular<\/em> build step into <em>angular.json<\/em>:<\/p>\n<pre class=\"lang:c# theme:vs2012\">\"assets\": [\r\n  { \"glob\": \"**\/*\", \"input\": \"node_modules\/ng2-pdfjs-viewer\/pdfjs\", \"output\": \"\/assets\/pdfjs\" },\r\n]<\/pre>\n<p>Or use <em>webpack<\/em> or similar bundlers (Hmm, are you still using webpack?)<\/p>\n<pre class=\"lang:c# theme:vs2012\">var TransferWebpackPlugin = require('transfer-webpack-plugin');\r\n...\r\nplugins: [\r\n  new TransferWebpackPlugin([\r\n\t{ from: 'node_modules\\ng2-pdfjs-viewer\\pdfjs', to: path.join(__dirname, 'assets') }\r\n  ])\r\n]<\/pre>\n<h2>PDF Loading Events<\/h2>\n<p>Often, you might like to tap into the PDF pipeline of printing or loading to execute a task. This could be to show a message to the user that the PDF is loaded for large PDFs or that the PDF is successfully printed, etc. The code found <a href=\"https:\/\/github.com\/intbot\/ng2-pdfjs-viewer\">here<\/a> provides several events hooks for that.<\/p>\n<h3>HTML<\/h3>\n<pre class=\"theme:vs2012-simple-talk lang:c# decode:true\">&lt;!-- your.component.html --&gt;\r\n&lt;ng2-pdfjs-viewer pdfSrc=\"gre_research_validity_data.pdf\"\r\n              \tviewerId=\"MyUniqueID\"\r\n              \t(onBeforePrint)=\"testBeforePrint()\"\r\n              \t(onAfterPrint)=\"testAfterPrint()\"\r\n              \t(onPagesLoaded)=\"testPagesLoaded($event)\"&gt;\r\n&lt;\/ng2-pdfjs-viewer&gt;<\/pre>\n<p>For events to work properly, you should set <code>viewerId<\/code>. This helps event routing understand which component the event is to be sent to, even if there is more than one <em>ng2-pdfjs-viewer<\/em> component on the page. <code>viewerId<\/code> should be a unique id (like a <code>guid<\/code>).<\/p>\n<h3>Angular component<\/h3>\n<p>Once capturing these events, developers can execute custom tasks\/code to take action based on these events. Given below is an event which emits and displays a number of pages in PDF.<\/p>\n<pre class=\"lang:c# theme:vs2012\">&lt;!-- your.component.ts --&gt;\r\npublic testBeforePrint() {\r\n\tconsole.log(\"testBeforePrint() successfully called\");\r\n}\r\npublic testAfterPrint() {\r\n\tconsole.log(\"testAfterPrint() successfully called\");\r\n}\r\npublic testPagesLoaded(count: number) {\r\n\tconsole.log(\"testPagesLoaded() successfully called. Total pages # : \" + count);\r\n}<\/pre>\n<p>You can see the events that are emitted on the developer console as shown below.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-94292\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/05\/graphical-user-interface-text-application-email.png\" alt=\"Image showing screenshot of DevTools. Important part highlighted: testPagesLoaded Total Page #4. TestBeforeProject, TestSfterPrint\" width=\"1033\" height=\"296\" \/><\/p>\n<h3>What about PDF files returned from an API?<\/h3>\n<p>The <em>ng2-pdfjs-viewer<\/em> component can also work with other server-side APIs to render returned PDFs. To get this going, you would convert the PDF into a <code>byte[] <\/code>array or <code>blob<\/code> and give it to the viewer.<\/p>\n<h3>A bit of HTML in an Angular component<\/h3>\n<pre class=\"theme:vs2012-simple-talk lang:c# decode:true\">&lt;!-- your.component.html --&gt;\r\n&lt;div style=\"height: 600px\"&gt;\r\n\t&lt;ng2-pdfjs-viewer #pdfViewer&gt;&lt;\/ng2-pdfjs-viewer&gt;\r\n&lt;\/div&gt;<\/pre>\n<p>Notice the <code>#pdfViewer<\/code>. This is your reference.<\/p>\n<h3>Some download code<\/h3>\n<p>The <code>downloadFile()<\/code> function calls the API; you may use any <em>HTTP<\/em> querying mechanism here. The requirement is that the API endpoint returns the pdf as a <code>byte<\/code> array.<\/p>\n<pre class=\"lang:c# theme:vs2012\">&lt;!-- your.component.ts --&gt;\t\r\n @ViewChild('pdfViewer') public pdfViewer;\r\nconstructor(private http: HttpClient) {\r\n\tlet url = \"api\/document\/getmypdf\";\r\n\tthis.downloadFile(url).subscribe(\r\n    \t(res) =&gt; {\r\n        \tthis.pdfViewer.pdfSrc = res; \/\/ pdfSrc can be Blob or Uint8Array\r\n        \tthis.pdfViewer.refresh(); \/\/ Ask pdf viewer to load\/refresh pdf\r\n    \t}\r\n\t);\r\n}\r\nprivate downloadFile(url: string): any {\r\n\treturn this.http.get(url, { responseType: 'blob' })\r\n    \t.pipe(\r\n        \tmap((result: any) =&gt; {\r\n            \treturn result;\r\n        \t})\r\n    \t);\r\n}<\/pre>\n<h2>A sample API using C# and ASP.NET Core<\/h2>\n<pre class=\"lang:c# theme:vs2012 \">[HttpGet]\r\n[Route(\"GetMyPdf\")]\r\npublic IActionResult GetMyPdf()\r\n{\r\n\tvar pdfPath = Path.Combine(Directory.GetCurrentDirectory(),\"sample.pdf\");\r\n\tbyte[] bytes = System.IO.File.ReadAllBytes(pdfPath);\r\n\treturn File(bytes, \"application\/pdf\");\r\n}<\/pre>\n<p>I used <em>ASP.NET Core<\/em> and some C# here. Don&#8217;t worry; you can do this in <em>Python<\/em> or <em>Ruby<\/em> or whatever server-side technology you prefer, or any APIs supplying pdf through HTTP endpoints.<\/p>\n<h2>Wrap up<\/h2>\n<p>The <a href=\"https:\/\/www.npmjs.com\/package\/ng2-pdfjs-viewer\"><em>ng2-pdfjs-viewer<\/em><\/a> component is a powerful tool to display PDF files without losing the user experience. If you are a student programmer who is building a pdf book library angular app or an enterprise programmer who wants to display a PDF stored away somewhere in SharePoint, this library is useful. The ability to show more than one PDF on the same page also helps with the comparison of different versions of a document. More on that later!<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This article by Aneesh Lal Gopalakrishnan describes the easiest way to integrate an inline pdf viewer into an angular application.&hellip;<\/p>\n","protected":false},"author":339718,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538,146992,53],"tags":[4143,146994,95506,4371,146995,146993,146996],"coauthors":[146982],"class_list":["post-94290","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","category-angular","category-featured","tag-net","tag-angular","tag-automate","tag-c","tag-ng2-pdfjs-viewer","tag-pdf","tag-pdfjs"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/94290","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/339718"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=94290"}],"version-history":[{"count":5,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/94290\/revisions"}],"predecessor-version":[{"id":94463,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/94290\/revisions\/94463"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=94290"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=94290"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=94290"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=94290"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}