10

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,

Ionut Ursan
  • 187
  • 1
  • 2
  • 11
  • Create a print directive, also use `angular.element` – tymeJV Oct 28 '13 at 14:25
  • wouldn't your code above cause all events/javascript data on said html to be lost? You could do essentially the same thing without losing said information by using an iframe or opening a new window in the same way. – Kevin B Oct 28 '13 at 14:26
  • Finally I've done it using the ng-hide directive(for hiding all the elements i don't wan't to print) and simply call windows.print() in a function in my controller. It seems to me that this is the best solution so far.. – Ionut Ursan Oct 29 '13 at 07:40
  • If you want to hide elements and prevent them from printing then use the media query print and hide the elements there like this: @media print { body * { visibility:hidden; } .class-to-print { visibility: visible; } } – Arne H. Bitubekk May 04 '15 at 13:50

3 Answers3

16

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.

Community
  • 1
  • 1
Mladen Danic
  • 3,600
  • 2
  • 25
  • 24
  • i tried lots of directive around the Google. This is fair easy to use and does it work. Thank you. – Wanny Miarelli Jan 08 '15 at 21:49
  • This is a clean solution and does not require jQuery plugins! Thanks, really useful – Michelangelo Mar 23 '15 at 11:15
  • @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. – Michelangelo Mar 25 '15 at 21:43
  • @Mikey Did you load your stylesheet inside the iFrame? (where it says `// Your styles here, I needed the margins set up like this`) – Mladen Danic Mar 26 '15 at 14:01
  • @MladenDanic Yes I did something like this: `` – Michelangelo Mar 26 '15 at 16:33
  • @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 Mar 26 '15 at 20:33
  • @MladenDanic I found a different solution which was much more work but delivers a nice PDF to the user. – Michelangelo Mar 30 '15 at 12:28
5

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();
                }
            });
        }
    };
});
Travis
  • 636
  • 8
  • 11
  • 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 Mar 26 '15 at 20:36
-1

angularPrint is an angular directive that's super easy to use. https://github.com/samwiseduzer/angularPrint

you simply decorate your html with its custom attributes:

<div>
  <div print-section>
    I'll print
    <p>Me, too!</p>
  </div>
  <div>I won't</div>
</div>
Bill Yang
  • 1,413
  • 17
  • 28