Building a Simple AngularJS Print Directive

August 9, 2014

36 comments

Building a Simple AngularJS Print DirectiveLast week I wrote a simple print button directive for an application that uses AngularJS as its client-side framework. The requirement of the button was to enable the printing of a DOM element that included some content. In this post I’m going to describe how I build this directive but I won’t cover what are AngularJS directives. There are many good online resources about AngularJS directives, so please read one of them before reading this post. The solution I use for the directive is based on this jsFiddle: http://jsfiddle.net/95ezN/121/.

The Directive Code

We will first start with the directive itself:

(function (angular) {
    'use strict';

    function printDirective() {
        var printSection = document.getElementById('printSection');

        // if there is no printing section, create one
        if (!printSection) {
            printSection = document.createElement('div');
            printSection.id = 'printSection';
            document.body.appendChild(printSection);
        }

        function link(scope, element, attrs) {
            element.on('click', function () {
                var elemToPrint = document.getElementById(attrs.printElementId);
                if (elemToPrint) {
                    printElement(elemToPrint);
                }
            });

            window.onafterprint = function () {
                // clean the print section before adding new content
                printSection.innerHTML = '';
            }
        }

        function printElement(elem) {
            // clones the element you want to print
            var domClone = elem.cloneNode(true);
            printSection.appendChild(domClone);
            window.print();
        }

        return {
            link: link,
            restrict: 'A'
        };
    }

    angular.module('app').directive('ngPrint', [printDirective]);
}(window.angular));

The code here is very simple and lets try to understand what it does:
When the directive is created, we first check if a print section DIV (which has the printSection id) is part of the DOM.  If the DIV doesn’t exists, we create it and append it to the body of the web page. That print section will be used for attaching DOM elements that we want to print.

Later on, we create the directive link function which receives both the element and its attributes as arguments. We expect that there will be a printElementId attribute in the button element. If there is no element then the printing functionality won’t work. We will wire to the button a click event handler which uses the printElement function to add the element that we want to print into the print section and then we use the window.print function to enable printing. Pay attention that we clone the DOM element that we want to print and then append the clone to the print section. We also make sure to clean the print section after printing occurred using the onafterprint event handler.

That is the part of the directive but we will need a little CSS magic to enable the print functionality.

The CSS Part

Once you have the directive in place, you will need the help of two media queries to achieve the printing functionality. If you just add the print section DIV without any CSS the whole web page will be printed. We want to print only the print section so here is a few lines of CSS that will help you to do that:

@media screen {
    #printSection {
        display: none;
    }
}

@media print {
    body * {
        visibility:hidden;
    }
    #printSection, #printSection * {
        visibility:visible;
    }
    #printSection {
        position:absolute;
        left:0;
        top:0;
    }
}

In the CSS, the printSection DIV is never shown on screen. Also, when printing occurs, the print media query will make sure not to display anything but the print section. It will also make sure that the print section is starting in the left top corner of the printing.

Using The Directive

After we added both the directive code and the CSS lets see how to use the directive:

<button class="btn btn-primary" ng-print print-element-id="printThisElement"><i class="fa fa-print"></i> Print</button>

In the button you will just put the ng-print directive and also the print-element-id attribute.

Summary

In the post I show a suggestion for a simple print button directive. Of course there are other ways to implement this functionality.
Feel free to use this code or to update it to your own needs.

UPDATE: You can find the directive in GitHub.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

36 comments

  1. Titus BlairNovember 21, 2014 ב 23:24

    Thanks for this great directive!

    Reply
  2. Filipe VieiraDecember 5, 2014 ב 18:25

    Hi, great directive, I just copy paste from here and it worked like a charm 😀

    Thanks.

    Reply
  3. jupDecember 15, 2014 ב 12:21

    You may want to clear the printSection on appending the element you want to print or else cancelling the print will retain the element in the printSection:


    function printElement(elem) {
    // clones the element you want to print
    var domClone = elem.cloneNode(true);
    printSection.innerHTML = '';
    printSection.appendChild(domClone);
    window.print();
    }

    Reply
    1. Gil Fink
      Gil FinkMarch 1, 2015 ב 9:01

      @jup,

      I’m doing that in the onafterprint event handler.

      Reply
    2. Martin GiannechiniDecember 16, 2015 ב 0:06

      This is the only solution work for me, thanks.

      Reply
  4. AndrewFebruary 27, 2015 ב 5:32

    This directive is broken. I have to do a relatively major reorganisation it to get it to work correctly.

    Reply
    1. Gil Fink
      Gil FinkMarch 1, 2015 ב 9:04

      @Andrew,

      I’m sorry for that.
      The same code is running perfectly in two production web applications so please check the version of AngularJS that you use (I was using 1.2 when I wrote the post) or the code you used.

      Reply
  5. ShannonApril 14, 2015 ב 19:21

    Thanks for the great directive Gil! I am getting multiple blank pages at the end for some reason, would you happen to know why that is?

    P.S.
    @Andrew, maybe don’t be so negative towards someone who posted code that makes peoples lives easier and saves you the time of writing a print directive yourself (and it’s obviously working for several people based on the comments). Wouldn’t it be more appropriate to say the directive isn’t working for you?

    Reply
  6. UpenApril 22, 2015 ב 8:55

    Hey Gil, i encountered an issue while printing on Google chrome as it prints extra two blank pages while in Mozilla it works fine.

    Is there anyone who is facing the same problem ? Kindly help if anyone is there.

    Reply
    1. sandeep jainApril 8, 2017 ב 10:00

      Hi, I am also facing the same issue , its print the extra blank page. in chrome in Firefox on both.
      Now its become the blocker for me.

      Please help me out !
      Thanks in advance!
      Sandeep Jain

      Reply
  7. Deepak KumarApril 27, 2015 ב 14:04

    This works fine if we do not have scrolling on the page which we are trying to print. If we have scroll, it print only those part which are visible on the screen when page loads

    Reply
    1. FaustoJune 30, 2015 ב 22:58

      The problem is in how some browsers handle visibility: hidden. Per the spec, this should cause the elements to continue to take up space. Although some browsers correct for this, they really shouldn’t (too much magic!). At first thought, I wanted to use display: none instead. But there’s no way to “reverse” this using pure CSS.

      Instead, I created an “outer” element that wraps everything but my #printSection. So the switch amounts to changing body * to #outer *. Then the rest of the code (the positioning hack, etc.) is no longer needed.

      Reply
  8. sachinMay 5, 2015 ב 10:08

    Thanks for this directive.
    But when i am printing div then URL is appearing in header footer.. can it be changed pragmatically..

    Many Thanks in advance..

    Reply
  9. LouisMay 13, 2015 ב 21:20

    Gil,

    This directive is great. I refactored it to be more TypeScripty and added typing to it. It works well for me. I have a couple problems…

    1) window.onafterprint isn’t supported in a number of browsers. See: https://goo.gl/W8sgFY As a consequence, the printSection is never cleaned up from click to click of the print button. Not sure of a way around that.

    2) I am trying to print out the contents of an angular ui-grid. If the grid has all the data visible, it works great. However, if data is scrolled outside the viewport, all that data isn’t printed.

    I think this is because the angular-ui-grid virtualizes its data and doesn’t render elements that are outside the scrollview viewport. I’m pondering ways around that….If I could turn off scroll virtualization, maybe that would work?

    At any rate this is good first crack at the printing problem. Thanks for contributing this to the world.

    Louis

    Reply
    1. LouisMay 15, 2015 ב 22:15

      For 1) above, the easy fix is to clear the div on the next print attempt and just forget about onafterprint. It isn’t supported well.

      2) This has nothing to do with ui-grid virtualization and everything to do with content that scrolls. Apparently other users see the problem. I don’t have any good solutions but at least I’m not after a red herring.

      Reply
  10. ManojJuly 17, 2015 ב 7:47

    Thanks for creating this awesome directive

    Reply
    1. Gil Fink
      Gil FinkJuly 21, 2015 ב 9:27

      @Manoj with pleasure.

      Reply
  11. BalaAugust 19, 2015 ב 10:50

    Thank you.

    Could you please let us know if there is any way to hide the url in the print page.

    In one of the blog read that need to change IE browser setting to disable header and footer in print page. But this cannot be done because of org policies etc..

    Is there any other way please help.

    Thanks in advance

    Reply
  12. TheoDecember 30, 2015 ב 17:50

    Hi Gil, thanks for this awesome directive. Would you be able to add this to a public repository, like Github, so we can all submit changes and improvements to it? It seems like if we work together we can all submit new features that can really make this thing a great solution. Eitherway, thanks for this!

    Reply
    1. Gil Fink
      Gil FinkDecember 30, 2015 ב 17:52

      Good point!
      I’ll add it to my Github and publish the link soon.

      Reply
      1. AliJanuary 15, 2016 ב 8:23

        I need help!
        Its working fine, and great work making it easy for others to use!
        But my content doesn’t fit in the print page, I need to fit all the content on the one page.
        Please tell how can I do this?

        Reply
        1. Gil Fink
          Gil FinkJanuary 15, 2016 ב 8:32

          Hi Ali,

          You have a media query for printing and you can set CSS for printing using that query (in the provided CSS you can see me using this media query). Using that knowledge you can manipulate the element to print and probably make it fit to one page.

          Hope it will help you!

          Reply
          1. AliJanuary 19, 2016 ב 14:47

            Thnaks Gil.
            I am running into another problem, however.
            The first time I print the page, it shows 2 pages: the second page is blank.
            If I cancel it, it stays in the memory or something like that, and when I press Print the second time, it shows the same page twice, i.e., 2 pages.
            I took the latest code from GitHub.
            Any idea what may be wrong?

          2. AliJanuary 20, 2016 ב 9:18

            Fixed the issues myself, and put changes on GitHub.

  13. ShriniFebruary 25, 2016 ב 23:13

    First of all, its a great directive for print that works in Chrome and Firefox, however in IE as soon as I click the print button, the elements on the origin page disappears. Not sure why it is designed that way. I can get around with commenting out the f.innerhtml = “” in this piece of code.

    function d(g) {
    var h = g.cloneNode(true);
    //f.innerHTML = “”;
    f.appendChild(h);
    window.print()
    }

    After commenting out that code, when I try to print my page that has ui-grid tables in it, for some reason, the table shrinks from its original size and the widths of the column shrinks to just the width of its contents. Not sure why it happens so with IE. May be the css files are somehow not getting injected into the DOM since this directive uses window.print(). I couldnt figure it out.

    Reply
    1. peterApril 10, 2016 ב 10:20

      Dear Gil,
      I want get dynamic data from DB, then create HTML, and perform call window.print().
      If use ng-print, we can do it?
      Please help me?

      Reply
      1. Gil Fink
        Gil FinkApril 10, 2016 ב 10:33

        Hi Peter,

        To be short, no.
        You will have to do a round trip to your server, fetch the data from the DB and generate the relevant HTML.
        If you use AngularJS in your front-end, you will be able to create a HTML that include the ng-print directive and then your user will be able to print.
        If you only want to use window.print function, you don’t need the ng-print directive.

        Reply
  14. RoshMay 4, 2016 ב 10:15

    Can we add the print directive in js, once the server return the value and call the call. That can be achieved ?

    Reply
    1. Gil Fink
      Gil FinkMay 4, 2016 ב 10:53

      @Rosh,

      I don’t understand your question.
      What do you want to achieve?

      Reply
      1. RajeshMay 4, 2016 ב 20:11

        Hi Gil,

        This works in demo/plunker but in my app, it prints the whole page, not the div section where I have the id specified.

        Do you know of any issues with Angular JS 1.5.6

        Thanks, Rajesh

        Reply
        1. Gil Fink
          Gil FinkMay 5, 2016 ב 9:08

          @Rajesh,

          I haven’t tested it in Angular 1.5.6 so I don’t know if there are issues.

          Reply
  15. Mahesha.sJanuary 5, 2017 ב 8:15

    yes ,its good work , but am not able to display data inside the input tag and img tag while printing , i have used ng-model for bind the data to input text box , ex:

    Company name:

    Reply
    1. Mahesha.sJanuary 5, 2017 ב 8:20

      This works in demo/plunker but in my app, it prints the whole page, not the div section where I have the id specified.
      Do you know of any issues with Angular JS 1.5.6

      same problem am facing in Angular JS 1.5.8

      Thanks & Regards,
      Mahesha.s

      Reply
  16. MahmoudApril 10, 2017 ב 13:29

    Hi,Great work .
    How can i add page number for each page?

    Reply