title: How can I output data to a pdf onclick in Angular?
body: <p>I'm trying to output an invoice to a pdf file. There are checkboxes that when checked will display the values in a modal after a button is clicked.</p>
  <p>Currently, I have everything in a modal but would like the option to have it be viewed as a pdf as well. I've been trying to use the jspdf library  but have been receiving an error when clicking the button to display the modal + output data to a pdf. This is the error in the console when I click the button to display the modal/create a PDF</p>
  <pre><code>ERROR TypeError: Cannot read properties of undefined (reading 'nativeElement')
      at ServicesComponent.submit (services.component.ts:55)
      at ServicesComponent_Template_form_submit_8_listener (services.component.html:18)
      at executeListenerWithErrorHandling (core.js:15308)
      at wrapListenerIn_markDirtyAndPreventDefault (core.js:15352)
      at HTMLFormElement.&lt;anonymous&gt; (platform-browser.js:561)
  </code></pre>
  <p>This is my services.component.ts where the submit function is located</p>
  <pre><code>import { Observable } from 'rxjs';
  import { MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
  import { ServiceHelper } from '../services.service';
  import { ServiceItem } from '../services.interface';
  import {MatCheckboxModule} from '@angular/material/checkbox';
  import {FormBuilder, FormGroup, FormArray, FormControl } from '@angular/forms';
  import {MatSnackBar} from '@angular/material/snack-bar';
  import { FormsModule } from '@angular/forms';
  import { Invoice } from '../invoice';
  import { AuthLayoutComponent } from './../auth-layout/auth-layout.component';
  import { jsPDF } from &quot;jspdf&quot;;
  import { Component, OnInit, Input, ElementRef, ViewChild } from '@angular/core';
  
  @Component({
    selector: 'app-services',
    templateUrl: './services.component.html',
    styleUrls: ['./services.component.css']
  })
  
  
  export class ServicesComponent {
  
    @ViewChild(&quot;output&quot;, {
      static: true
    }) output: ElementRef;
  
    serviceItems: ServiceItem[];
    invoice: Invoice; //from invoice.ts
  
    constructor(private dialog: MatDialog, private serviceHelper: ServiceHelper, private _snackBar: MatSnackBar) {
  
      this.invoice = new Invoice();
      this.serviceItems = serviceHelper.getServices();
    }
  
    clearInvoice(): void {
      /**
       * Iterate over the beverages array and set each objects checked property to false.
       */
      for (let service of this.serviceItems) {
        service.checked = false;
      }
  
      this.invoice = new Invoice(); // return a new instance of the Bill object.
    }
  
  
  
    openSnackBar(message: string) {
      this._snackBar.open(message + &quot; was added!&quot;);
    }
  
    //Opens modal with calculated results
    submit() {
  
      //Attempting to create pdf
      const DATA = this.output.nativeElement;
      const doc: jsPDF = new jsPDF(&quot;p&quot;, &quot;mm&quot;, &quot;a4&quot;);
      doc.html(DATA, {
        callback: (doc) =&gt; {
          doc.output(&quot;dataurlnewwindow&quot;);
        }
      });
  
  
      console.log(this.serviceItems)
  
  
      for (let item of this.serviceItems) {
        if (item.checked) {
          this.invoice.addServiceItem(item);
        }
      }
  
      console.log(this.invoice.serviceItems);
      console.log(this.invoice.getTotal());
  
  
      const dialogRef = this.dialog.open(AuthLayoutComponent, {
        data: {
          invoice: this.invoice
        },
        disableClose: true,
        width: '800px'
      })
  
  
      dialogRef.afterClosed().subscribe(result =&gt; {
        if (result === 'confirm') {
          alert('Your order has been processed!');
          this.clearInvoice();
        } else {
          alert('Your order has been canceled.');
          this.clearInvoice();
        }
      })
    }
  }
  </code></pre>
  <p>here's my auth-layout.component.html that contains the modal markup</p>
  <pre><code>&lt;h2 mat-dialog-title&gt;Order Summary&lt;/h2&gt;
  &lt;div mat-dialog-content id=&quot;output&quot; #output&gt;
      &lt;div&gt;
          &lt;strong&gt;Menu items:&lt;/strong&gt;
          &lt;div *ngFor=&quot;let item of invoice.serviceItems&quot;&gt;
              &amp;nbsp;{{ item.serviceName }} ({{item.servicePrice | currency}})
          &lt;/div&gt;
          &lt;br /&gt;
          Subtotal: {{ invoice.getTotal() | currency }}
      &lt;/div&gt;
  
      &lt;hr /&gt;
  
      &lt;p&gt;
      Labor Cost: {{ invoice.getLaborTotal() | currency }}
      &lt;/p&gt;
  
      &lt;hr /&gt;
  
      &lt;p style=&quot;font-weight: bold;&quot;&gt;Invoice total: {{ invoice.getInvoice() | currency }}&lt;/p&gt;
  &lt;/div&gt;
  </code></pre>
  <p>and my auth-layout.component.ts file for the modal</p>
  <pre><code>import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
  import { Component, Inject, OnInit } from '@angular/core';
  import { Invoice } from '../invoice';
  import { jsPDF } from &quot;jspdf&quot;;
  
  
  @Component({
    selector: 'app-auth-layout',
    templateUrl: './auth-layout.component.html',
    styleUrls: ['./auth-layout.component.css']
  })
  export class AuthLayoutComponent implements OnInit {
  
    invoice: Invoice;
  
    constructor(private dialogRef: MatDialogRef&lt;AuthLayoutComponent&gt;, @Inject(MAT_DIALOG_DATA) data: any) {
      this.invoice = data.invoice;
      console.log(this.invoice);
    }
    ngOnInit(): void {
    }
  }
  </code></pre>