57

I want to print HTML template in angular 2. I had explored about this I got solution in angularjs 1 Print Html Template in Angularjs 1

Any suggestion would be appreciated

Neeraj Rathod
  • 1,609
  • 4
  • 18
  • 25

11 Answers11

100

That's how I've done it in angular2 (it is similar to that plunkered solution) In your HTML file:

<div id="print-section">
  // your html stuff that you want to print
</div>
<button (click)="print()">print</button>

and in your TS file :

print(): void {
    let printContents, popupWin;
    printContents = document.getElementById('print-section').innerHTML;
    popupWin = window.open('', '_blank', 'top=0,left=0,height=100%,width=auto');
    popupWin.document.open();
    popupWin.document.write(`
      <html>
        <head>
          <title>Print tab</title>
          <style>
          //........Customized style.......
          </style>
        </head>
    <body onload="window.print();window.close()">${printContents}</body>
      </html>`
    );
    popupWin.document.close();
}

UPDATE:

You can also shortcut the path and use merely ngx-print library for less inconsistent coding (mixing JS and TS) and more out-of-the-box controllable and secured printing cases.

SeleM
  • 9,310
  • 5
  • 32
  • 51
  • 4
    is there a way to easily include the styles if printContents as appeared in the original site screen? – amit Jun 13 '17 at 09:22
  • @amit actually I didnt try to think about it. – SeleM Jun 13 '17 at 11:10
  • @selemmn i spend many hours trying to figure it out without success. If you do find a way, i would love to know – amit Jun 13 '17 at 11:14
  • @amit could you provide a plunker for your issue ? – SeleM Jun 13 '17 at 12:25
  • @selemmn it is a bit hard because it is a complex angular 2 project with many components and many styles... – amit Jun 13 '17 at 12:40
  • I tried this. but some how am facing the below problem Can please please help me whats going wrong. – blackdaemon Jun 18 '17 at 05:28
  • https://stackoverflow.com/questions/44558147/how-to-remove-html-print-background-showing-content/44558293?noredirect=1#comment76124985_44558293 – blackdaemon Jun 18 '17 at 05:29
  • Hey, would you know how to make this work with images too? – Antoine Dussarps Jul 18 '17 at 13:45
  • @AntoineDussarps `printContents = document.getElementById('print-section').src;` ? – SeleM Jul 19 '17 at 18:38
  • i have a form with text field when i enter data in txt field does not shown in page – nilesh Nov 14 '17 at 13:14
  • @nilesh a plnkr or stackblitz please ? – SeleM Nov 14 '17 at 20:41
  • Works fine, I just wonder if there is a change to use existing styles somehow :) – Andurit Nov 20 '17 at 16:11
  • I cannot get this to work with reactive forms in angular2. Can someone please help? https://stackoverflow.com/questions/50560640/print-functionality-in-angular4-reactive-form-values-are-not-shown/50560795?noredirect=1#comment88132538_50560795 – RemyaJ May 28 '18 at 09:52
  • This is a javascript version. Since angular is in typescript, can we have a typescript version for this one ? – Rahul Sharma Jan 10 '19 at 13:46
  • @RahulSharma i'd already developed a directive `ngx-print`, it is nothing as size, feel free to use it (i'd mentioned that in the update section in my answer) – SeleM Jan 10 '19 at 15:35
  • You should not access the DOM directly... "document.getElementById('print-section').innerHTML" – Cody Tolene Jan 30 '19 at 20:27
  • This is great also. Is their a way to remove that background page opened behind the print dialog? It seems to open a page first with the HTML content, then puts the print dialog over it. – RoadRunner Mar 13 '19 at 01:42
  • @RoadRunner could you elaborate more ? – SeleM Mar 13 '19 at 10:33
  • image is not getting printed with ngx-print..anyone has any idea how to do that ? – Rak2018 Jun 20 '19 at 21:36
  • Hi @Rak2018, open a described issue and join a stackblitz link on the github repository please ! – SeleM Jun 21 '19 at 09:05
  • https://stackoverflow.com/questions/56694343/problem-with-image-printing-using-ngx-print?noredirect=1#comment100005253_56694343 I opened this issue with code and the problem..will create a stackblitz soon – Rak2018 Jun 24 '19 at 15:05
  • @Rak2018, stackblitz ? – SeleM Jun 25 '19 at 07:48
  • I am trying to print a table with 50 column but on print document template it displays only 20 columns rest of all are hidden. Could you please help me how to solve this issue and display entire table's column in single row? – aman jain Jul 16 '19 at 11:54
  • Does not open print dialog in IE 11 for me – iMalek Feb 24 '20 at 16:32
  • @SeleM-sir i have used above code but i received null in line-window.open its returning null..? – Kapil Soni Oct 20 '21 at 05:19
  • Worked like charm. Thanks – Anand_5050 Mar 05 '22 at 13:35
27

In case anyone else comes across this problem, if you have already laid the page out, I would recommend using media queries to set up your print page. You can then simply attach a print function to your html button and in your component call window.print();

component.html:

<div class="doNotPrint">
    Header is here.
</div>

<div>
    all my beautiful print-related material is here.
</div>

<div class="doNotPrint">
    my footer is here.
    <button (click)="onPrint()">Print</button>
</div>

component.ts:

onPrint(){
    window.print();
}

component.css:

@media print{
  .doNotPrint{display:none !important;}
}

You can optionally also add other elements / sections you do not wish to print in the media query.

You can change the document margins and all in the print query as well, which makes it quite powerful. There are many articles online. Here is one that seems comprehensive: https://www.sitepoint.com/create-a-customized-print-stylesheet-in-minutes/ It also means you don't have to create a separate script to create a 'print version' of the page or use lots of javascript.

Sabyasachi Patra
  • 650
  • 4
  • 12
Farasi78
  • 981
  • 10
  • 18
  • 3
    You could also use the :not() selector from css thus, using only a class on the element that will be printed. Example: then on css file: @media print{ :not(.print_this){display:none;!important} } – Charleston Mar 19 '18 at 17:06
  • How could I use this to print the content of a sub-child div? Hierarchically I have this situation: MainComponent > DashboardComponent > DivToPrint. It's a chain of router-outlets. How could I set this up to use this @media query? – Luca Effe Federzoni Apr 23 '19 at 07:34
  • @LucaEffeFederzoni I believe it works regardless of your hierarchy, as long as you mark each div appropriately. I.e. in DashboardComponent, mark everything that should not be printed as 'do not print', it can get a bit complicated, and in that case, you may want to do what others suggest which is to create a single page for printing. If you use Angular, To avoid repeating CSS, put the do not print rule in your app.component.css and add it to the styleUrls for each component you need it. – Farasi78 Apr 23 '19 at 12:54
  • @Farasi78 It works indeed, but not entirely. I created a directive that prints the contents of the dive which is applied but the print is a bit odd. Thanks for the help by the way, at least I managed to progress. – Luca Effe Federzoni Apr 23 '19 at 13:31
  • I am trying to print a table with 50 column but on print document template it displays only 20 columns rest of all are hidden. Could you please help me how to solve this issue and display entire table's column in single row? – aman jain Jul 16 '19 at 11:55
11

you can do like this in angular 2

in ts file

 export class Component{          
      constructor(){
      }
       printToCart(printSectionId: string){
        let popupWinindow
        let innerContents = document.getElementById(printSectionId).innerHTML;
        popupWinindow = window.open('', '_blank', 'width=600,height=700,scrollbars=no,menubar=no,toolbar=no,location=no,status=no,titlebar=no');
        popupWinindow.document.open();
        popupWinindow.document.write('<html><head><link rel="stylesheet" type="text/css" href="style.css" /></head><body onload="window.print()">' + innerContents + '</html>');
        popupWinindow.document.close();
  }

 }

in html

<div id="printSectionId" >
  <div>
    <h1>AngularJS Print html templates</h1>
    <form novalidate>
      First Name:
      <input type="text"  class="tb8">
      <br>
      <br> Last Name:
      <input type="text"  class="tb8">
      <br>
      <br>
      <button  class="button">Submit</button>
      <button (click)="printToCart('printSectionId')" class="button">Print</button>
    </form>
  </div>
  <div>
    <br/>
   </div>
</div>
Neeraj Rathod
  • 1,609
  • 4
  • 18
  • 25
Vikash Dahiya
  • 5,741
  • 3
  • 17
  • 24
7

EDIT: updated the snippets for a more generic approach

Just as an extension to the accepted answer,

For getting the existing styles to preserve the look 'n feel of the targeted component, you can:

  1. make a query to pull the <style> and <link> elements from the top-level document

  2. inject it into the HTML string.

To grab a HTML tag:

private getTagsHtml(tagName: keyof HTMLElementTagNameMap): string
{
    const htmlStr: string[] = [];
    const elements = document.getElementsByTagName(tagName);
    for (let idx = 0; idx < elements.length; idx++)
    {
        htmlStr.push(elements[idx].outerHTML);
    }

    return htmlStr.join('\r\n');
}

Then in the existing snippet:

const printContents = document.getElementById('print-section').innerHTML;
const stylesHtml = this.getTagsHtml('style');
const linksHtml = this.getTagsHtml('link');

const popupWin = window.open('', '_blank', 'top=0,left=0,height=100%,width=auto');
popupWin.document.open();
popupWin.document.write(`
    <html>
        <head>
            <title>Print tab</title>
            ${linksHtml}
            ${stylesHtml}
            ^^^^^^^^^^^^^ add them as usual to the head
        </head>
        <body onload="window.print(); window.close()">
            ${printContents}
        </body>
    </html>
    `
);
popupWin.document.close();

Now using existing styles (Angular components create a minted style for itself), as well as existing style frameworks (e.g. Bootstrap, MaterialDesign, Bulma) it should look like a snippet of the existing screen

3

Print service

import { Injectable } from '@angular/core';

@Injectable()
export class PrintingService {

public print(printEl: HTMLElement) {
    let printContainer: HTMLElement = document.querySelector('#print-container');

    if (!printContainer) {
      printContainer = document.createElement('div');
      printContainer.id = 'print-container';
    } 

    printContainer.innerHTML = '';

    let elementCopy = printEl.cloneNode(true);
    printContainer.appendChild(elementCopy);
    document.body.appendChild(printContainer);

    window.print();
  }
}

Сomponent that I want to print

@Component({
  selector: 'app-component',
  templateUrl: './component.component.html',
  styleUrls: ['./component.component.css'],
  encapsulation: ViewEncapsulation.None
})
export class MyComponent {
  @ViewChild('printEl') printEl: ElementRef;

  constructor(private printingService: PrintingService) {}

  public print(): void {
    this.printingService.print(this.printEl.nativeElement);
 }

}

Not the best choice, but works.

Kliment Ru
  • 2,057
  • 13
  • 16
3

Use the library ngx-print.

Installing:

yarn add ngx-print
or
npm install ngx-print --save

Change your module:

import {NgxPrintModule} from 'ngx-print';
...
imports: [
    NgxPrintModule,
...

Template:

<div id="print-section">
  // print content
</div>
<button ngxPrint printSectionId="print-section">Print</button>

More details

progm
  • 2,782
  • 3
  • 14
  • 32
1

I ran into the same issue and found another way to do this. It worked for in my case as it was a relatively small application.

First, the user will a click button in the component which needs to be printed. This will set a flag that can be accessed by the app component. Like so

.html file

<button mat-button (click)="printMode()">Print Preview</button>

.ts file

  printMode() {
    this.utilities.printMode = true;
  }

In the html of the app component, we hide everything except the router-outlet. Something like below

<div class="container">       
  <app-header *ngIf="!utilities.printMode"></app-header>
  <mat-sidenav-container>
    <mat-sidenav *ngIf="=!utilities.printMode">
      <app-sidebar></app-sidebar>
    </mat-sidenav>
    <mat-sidenav-content>
      <router-outlet></router-outlet>
    </mat-sidenav-content>
  </mat-sidenav-container>
</div>

With similar ngIf conidtions, we can also adjust the html template of the component to only show or hide things in printMode. So that the user will see only what needs to get printed when print preview is clicked.

We can now simply print or go back to normal mode with the below code

.html file

<button mat-button class="doNotPrint" (click)="print()">Print</button>
<button mat-button class="doNotPrint" (click)="endPrint()">Close</button>

.ts file

  print() {
    window.print();
  }

  endPrint() {
    this.utilities.printMode = false;
  } 

.css file (so that the print and close button's don't get printed)

@media print{
   .doNotPrint{display:none !important;}
 }
1

Shortest solution to be assign the window to a typescript variable then call the print method on that, like below

in template file

<button ... (click)="window.print()" ...>Submit</button>

and, in typescript file

window: any;
constructor() {
  this.window = window;
}
0

The best option I found to solve this without getting into trouble with my styles was using a separate route for my printed output and load this route into an iframe.

My surrounding component is shown as a tab page.

@Component({
  template: '<iframe id="printpage" name="printpage" *ngIf="printSrc" [src]="printSrc"></iframe>',
  styleUrls: [ 'previewTab.scss' ]
})
export class PreviewTabPage {
  printSrc: SafeUrl;

  constructor(
    private navParams: NavParams,
    private sanitizer: DomSanitizer,
  ) {
    // item to print is passed as url parameter
    const itemId = navParams.get('itemId');

    // set print page source for iframe in template
    this.printSrc = this.sanitizer.bypassSecurityTrustResourceUrl(this.getAbsoluteUrl(itemId));
  }

  getAbsoluteUrl(itemId: string): string {
    // some code to generate an absolute url for your item
    return itemUrl;
  }
}

The iframe just loads the print route that renders a print component in the app. In this page the print might be triggered after the view is fully initialized. Another way could be a print button on the parent component that triggers the print on the iframe by window.frames["printpage"].print();.

@Component({
  templateUrl: './print.html',
  styleUrls: [ 'print.scss' ]
})
export class PrintPage implements AfterViewInit {

  constructor() {}

  ngAfterViewInit() {
    // wait some time, so all images or other async data are loaded / rendered.
    // print could be triggered after button press in the parent component as well.
    setTimeout(() => {
      // print current iframe
      window.print();
    }, 2000);
  }

}
Dave Gööck
  • 395
  • 1
  • 15
0

Windows applications usually come with build in print capability, but for a web application, I would choose to simply generate a PDF file.

The simplest way that I found is to generate a PDf file using PDFMake (www.pdfmake.org). You can then offer the user the choice to open or download the generated PDF file.

0

If you need to print some custom HTML, you can use this method:

ts:

    let control_Print;

    control_Print = document.getElementById('__printingFrame');

    let doc = control_Print.contentWindow.document;
    doc.open();
    doc.write("<div style='color:red;'>I WANT TO PRINT THIS, NOT THE CURRENT HTML</div>");
    doc.close();

    control_Print = control_Print.contentWindow;
    control_Print.focus();
    control_Print.print();

html:

<iframe title="Lets print" id="__printingFrame" style="width: 0; height: 0; border: 0"></iframe>
Pablo Chvx
  • 1,809
  • 18
  • 31