Is there an Angular way of printing a div from a HTML page?

22,654

Solution 1

I have created a simple directive for printing div contents using the iframe technique. It's a bit hackish, but works very well. there is no better way to do it in my opinion. The directive script is:

'use strict';

angular.module('yourAppNameHere').directive('printDiv', function () {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      element.bind('click', function(evt){    
        evt.preventDefault();    
        PrintElem(attrs.printDiv);
      });

      function PrintElem(elem)
      {
        PrintWithIframe($(elem).html());
      }

      function PrintWithIframe(data) 
      {
        if ($('iframe#printf').size() == 0) {
          $('html').append('<iframe id="printf" name="printf"></iframe>');  // an iFrame is added to the html content, then your div's contents are added to it and the iFrame's content is printed

          var mywindow = window.frames["printf"];
          mywindow.document.write('<html><head><title></title><style>@page {margin: 25mm 0mm 25mm 5mm}</style>'  // Your styles here, I needed the margins set up like this
                          + '</head><body><div>'
                          + data
                          + '</div></body></html>');

          $(mywindow.document).ready(function(){
            mywindow.print();
            setTimeout(function(){
              $('iframe#printf').remove();
            },
            2000);  // The iFrame is removed 2 seconds after print() is executed, which is enough for me, but you can play around with the value
          });
        }

        return true;
      }
    }
  };
});

In your template you mark the print button like this:

<a href="#" print-div=".css-selector-of-the-div-you-want-to-print">Print!</a>

And that's it, it should work. The hackish part is that the iFrame is removed after a certain number of miliseconds as there is no cross-browser way to detect the end of the print execution, so you guestimate how much time would be needed for it to run.

You may need to include jQuery for it to work, I'm not sure as I almost never work without it. I added some inline comments to make things clearer. I used some code from this answer that uses a popup to print div content (but outside of Angular.js). Maybe you'll like that approach more.

Solution 2

I needed this to work in IE8+

'use strict';

angular
.module('print-div', [])
.directive('printDiv', function () {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {

            var iframe;
            var elementToPrint = document.querySelector(attrs.printDiv);

            if (!window.frames["print-frame"]) {
                var elm = document.createElement('iframe');
                elm.setAttribute('id', 'print-frame');
                elm.setAttribute('name', 'print-frame');
                elm.setAttribute('style', 'display: none;');
                document.body.appendChild(elm);
            }

            function write(value) {
                var doc;
                if (iframe.contentDocument) { // DOM
                    doc = iframe.contentDocument;
                } else if (iframe.contentWindow) { // IE win
                    doc = iframe.contentWindow.document;
                } else {
                    alert('Wonder what browser this is... ' + navigator.userAgent);
                }
                doc.write(value);
                doc.close();
            }

            element.bind('click', function(event) {
                iframe = document.getElementById('print-frame');
                write(elementToPrint.innerHTML);

                if (window.navigator.userAgent.indexOf ("MSIE") > 0) {
                    iframe.contentWindow.document.execCommand('print', false, null);
                } else {
                    iframe.contentWindow.focus();
                    iframe.contentWindow.print();
                }
            });
        }
    };
});
Share:
22,654
Ionut Ursan
Author by

Ionut Ursan

Updated on August 16, 2022

Comments

  • Ionut Ursan
    Ionut Ursan almost 2 years

    I have an html page using AngularJS, and I want to print a div from it. Is there an Angular way of doing it? .. or it`s just the classic javascript way below:

        <script language="javascript" type="text/javascript">
        function printDiv(divID) {
            //Get the HTML of div
            var divElements = document.getElementById(divID).innerHTML;
            //Get the HTML of whole page
            var oldPage = document.body.innerHTML;
    
            //Reset the page's HTML with div's HTML only
            document.body.innerHTML =
            "<html><head><title></title></head><body>" +
            divElements + "</body>";
    
            //Print Page
            window.print();
    
            //Restore orignal HTML
            document.body.innerHTML = oldPage;
            }
        </script> 
    

    If AngularJS doesn't have anything around this, can you suggest o way of doing it, without using JavaScript functions (ex: doc.getElementById() ).. an approach close to the AngularJS way ?

    Thx,

  • Wanny Miarelli
    Wanny Miarelli over 9 years
    i tried lots of directive around the Google. This is fair easy to use and does it work. Thank you.
  • Michelangelo
    Michelangelo about 9 years
    This is a clean solution and does not require jQuery plugins! Thanks, really useful
  • Michelangelo
    Michelangelo about 9 years
    @MladenDanic Although I really like the solution, I can not seem to load external css styling sheets. I am forced to use the html styling tags. Any way around this problem? I have a lot of styling to do :)...It becomes messy fast.
  • Mladen Danic
    Mladen Danic about 9 years
    @Mikey Did you load your stylesheet inside the iFrame? (where it says // Your styles here, I needed the margins set up like this)
  • Michelangelo
    Michelangelo about 9 years
    @MladenDanic Yes I did something like this: <html><head><title></title><link href="css/print.css" >
  • Michelangelo
    Michelangelo about 9 years
    @MladenDanic Also, another problem I encounterd is that it does not work in IE11. I tries to open an iframe but fails...So answer below is also true.
  • Michelangelo
    Michelangelo about 9 years
    I can't believe this. IE is always a pain. The first answer seemed to work untill I tested this in IE. Does this do job well? I mean no problems in other browsers?
  • Michelangelo
    Michelangelo about 9 years
    @MladenDanic I found a different solution which was much more work but delivers a nice PDF to the user.