56

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

11 Answers 11

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.

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

25 Comments

is there a way to easily include the styles if printContents as appeared in the original site screen?
@amit actually I didnt try to think about it.
@selemmn i spend many hours trying to figure it out without success. If you do find a way, i would love to know
@amit could you provide a plunker for your issue ?
@selemmn it is a bit hard because it is a complex angular 2 project with many components and many styles...
|
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.

5 Comments

You could also use the :not() selector from css thus, using only a class on the element that will be printed. Example: <div class="print_this"> something to print </div> then on css file: @media print{ :not(.print_this){display:none;!important} }
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?
@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 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.
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?
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>

5 Comments

Your answer needs some correction, You were very close. thank for your quick response, I upvoted answer :)
Actually i gave this answer without testing , i will update it soon
I have updated it, It's working for me. test it, do the correction if needed
if i enter any text in text feild it not printed
@VikashDahiya sir i have used above code but i received null in line-window.open its returning null..
7

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

Comments

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.

2 Comments

Isn't this going to print the whole page instead of juts that element?
don't forget to add css @media print and play with display and visibility to make the above code works
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

3 Comments

Can you provide details on custom css file
yes, you can add custom style or existing style. Check ngx-print documentation
My IDE says attribute ngxPrint is not allowed here. It compiles without errors, but the button does not do anything.
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;}
 }

Comments

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;
}

Comments

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);
  }

}

Comments

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.

Comments

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>

Comments

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.