Observing print events

Posted on May 7, 2021

There’s an Angular application which needs a different layout for its component when the user asks for a printed version. Yes, you can plain the media queries, but they’re not enough in certain cases. That’s why I had to find a different way to archieve the goal.

On print requests, the browser is emitting two events: beforeprint and afterprint. I thought that listening to those ones could be a possible solution: the application can alter its view on beforeprint, to restore the regular approach on the next afterprint. So I’ve build this small helper function.

export const observePrintEvents = (win: Window): Observable<'beforeprint' | 'afterprint'> =>
  merge(
    fromEvent(win, 'beforeprint').pipe(mapTo<Event, 'beforeprint'>('beforeprint')),
    fromEvent(win, 'afterprint').pipe(mapTo<Event, 'afterprint'>('afterprint'))
  );

To make it easier, I also introduced another helper on the top of the previous one.

export const printMode = (win: Window) => observePrintEvents(win)
  .pipe(map(event => event === 'beforeprint'), startWith(false));

Things like these can be successfully used in a template (through async pipe) to match the expectation. Think about an *ngIf directive which will show or hide a certain component for print-layout only.

But there’s a problem: window.print method we call to trigger the print programmatically is blocking the JavaScript execution. That’s not an issue while using the browser menu or the shortcuts (e.g. CTRL + P on Windows), but it is when triggering the print within an event handler (like listening to a button click). So, that’s the definitive way of raising a print request!

export const printWindow = (win: Window) => {
  win.dispatchEvent(new Event('beforeprint'));
  setTimeout(() => { win.print(); });
};

The main idea is to programmatically dispatch a beforeprint event and then putting a window.print in the event queue. In this way the page will have the chance to adapt before being blocked for the printing process. You can have a look here for a working example. Hope you’ll enjoy!