Ionic 5 with Angular 9: How to Print from Desktop (not mobile device)

Viewed 1928

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?

3 Answers

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

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.

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.

Related