5

I'm dealing with an ionic 5 app that was built as a web app. There's no mobile component to it.

I need to allow users to print a page. When I have a print button and associated with a function like the one below, it only prints the viewable area.

printPage() {
  window.print();
}

Given the amount of content on the screen, I can tell that this document should be 2-3 pages long.

I see that there's a cordova-plugin-printer plugin, but it's meant for printing from a mobile device.

What's the proper way to get the entire DOM printed?

Ben Downey
  • 2,575
  • 4
  • 37
  • 57
  • `window.print()` should launch the browser's print dialog, which allows the user to print the whole document, does it not? – Will Alexander Jul 30 '20 at 18:18
  • It does not. I've tested in Chrome and Firefox. The dialog box indicates that it's only printing the viewable area. I suspect there's something ionic is doing here but I'm not sure. This should be a relatively straightforward command. – Ben Downey Jul 30 '20 at 18:33
  • There's a potentially useful answer here: https://stackoverflow.com/questions/10845621/window-print-is-not-printing-the-whole-page – Will Alexander Jul 30 '20 at 18:36
  • Yeah, I found that in my googling but it had no effect. – Ben Downey Jul 30 '20 at 20:44
  • Ionic has a lot of flexbox and overflow hidden CSS on its various page and toolbar and content components. I'm guessing a print media query would need to be added, and undo that CSS. Not sure what all of them are since it depends on what you have in your app. Hope that can help point you in the right direction – cjd82187 Jul 31 '20 at 01:25
  • This goes beyond flexbox and overflow issues. No matter what part of the screen is in view, I can print it. The problem is that there seem to be no way for `window.print()` to capture the entire HTML document. I suspect there's something going on with the shadow Dom, but I'm not sure. – Ben Downey Jul 31 '20 at 19:47

3 Answers3

3

That might happen, because of a scrollable div, or something like that, nested in the body.

To get around this, you could manually select what to print by doing something like this answer from another StackOverflow question:

printPage() {
    var mywindow = window.open('', 'PRINT', 'height=400,width=600');

    mywindow.document.write('<html><head><title>' + document.title  + '</title>');
    mywindow.document.write('</head><body>');
    mywindow.document.write(document.getElementById('#printArea').innerHTML);
    mywindow.document.write('</body></html>');

    mywindow.document.close(); // necessary for IE >= 10
    mywindow.focus(); // necessary for IE >= 10*/

    mywindow.print();
    mywindow.close();
}

Link to the original answer: https://stackoverflow.com/a/2255438/9150652

MauriceNino
  • 6,214
  • 1
  • 23
  • 60
2

I recently faced a similar situation and I ended up creating an angular component in my project, which is inspired by e-ngx-print.

The only limitation is that you will have to pass a *.css file to this component for the section you want to print.

I have created a stackblitz for you, have a look and see if it's useful for you.

GoWin
  • 338
  • 3
  • 15
0

I tried first the sweet solution provided by @MauriceNino but noticed some throwback such as not preserving styles and fonts.

Therefore I opened an issue (that has been closed in favor of this issue) in the Ionic repo and developed the following workaround.

Basically, instead of copying the content in a popup, I clone the targeted node that contains the content at the body root and remove it after print or when the use click the "cancel" button in the print modal.

export const print = ({element}: {element: Node}) => {
  const body: HTMLBodyElement | null = document.querySelector('body');

  if (!body) {
    return;
  }

  const appRoot: HTMLElement | null = body.querySelector('app-root');
  let node: Node | null | undefined = undefined;

  window.addEventListener(
    'afterprint',
    () => {
      if (node) {
        body.removeChild(node);
      }

      appRoot?.classList.remove('hidden');
    },
    {once: true}
  );

  const onRender = async (_mutations: MutationRecord[], observer: MutationObserver) => {
    observer.disconnect();

    // Here you can do other things you need such as lazy loading content if you need too

    appRoot?.classList.add('hidden');

    window.print();
  };

  const docObserver: MutationObserver = new MutationObserver(onRender);
  docObserver.observe(body, {childList: true, subtree: true});

  node = body?.appendChild(element.cloneNode(true));
};

In addition, while printing, I hide the Ionic nodes. To do so I also defined following css (I am in a Stencil app that's why I target app-root):

@media print {
  app-root.hidden {
    display: none;
  }
}

Note the most beautiful hack but, does the job. Hopefully the issue will be solved soon.

David Dal Busco
  • 7,975
  • 15
  • 55
  • 96