Syncfusion AI Assistant

How can I help you?

How to Customize the context menu in PDF Viewer in Angular

17 Apr 202612 minutes to read

The PDF Viewer supports extensive customization of the context menu, including reaching specific goals like adding new items, hiding default options, and handling custom click events.

Add Custom Context Menu Items

You can add custom options to the context menu using the addCustomMenu() method. This is typically implemented during the documentLoad event.

Implementation Guide

  1. Define the menu items as an array of objects.
  2. Call the addCustomMenu method within the documentLoad event handler.
import { Component, ViewChild } from '@angular/core';
import {
  PdfViewerComponent,PdfViewerModule,ToolbarService,
  NavigationService,AnnotationService,LinkAnnotationService,
  BookmarkViewService,ThumbnailViewService,PrintService,
  TextSelectionService,TextSearchService,
  FormFieldsService,FormDesignerService,
} from '@syncfusion/ej2-angular-pdfviewer';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [PdfViewerModule],
  providers: [
    ToolbarService,NavigationService,AnnotationService,
    LinkAnnotationService,BookmarkViewService,ThumbnailViewService,
    PrintService,TextSelectionService,
    TextSearchService,FormFieldsService,
    FormDesignerService,
  ],
  template: `
    <ejs-pdfviewer
      #pdfviewer
      id="PdfViewer"
      [documentPath]="document"
      [resourceUrl]="resource"
      (documentLoad)="documentLoad()"
      (customContextMenuSelect)="customContextMenuSelect($event)"
      (customContextMenuBeforeOpen)="customContextMenuBeforeOpen($event)"
      style="height: 100vh; width: 100%; display: block"
    >
    </ejs-pdfviewer>
  `,
})
export class AppComponent {
  @ViewChild('pdfviewer')
  public pdfviewerObj!: PdfViewerComponent;

  public document = 'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf';

  public resource =
    'https://cdn.syncfusion.com/ej2/32.2.3/dist/ej2-pdfviewer-lib';

  public menuItems = [
    {
      text: 'Search In Google',
      id: 'search_in_google',
      iconCss: 'e-icons e-search',
    },
    {
      text: 'Lock Annotation',
      id: 'lock_annotation',
      iconCss: 'e-icons e-lock',
    },
    {
      text: 'Unlock Annotation',
      id: 'unlock_annotation',
      iconCss: 'e-icons e-unlock',
    },
    {
      text: 'Lock Form Field',
      id: 'read_only_true',
      iconCss: 'e-icons e-lock',
    },
    {
      text: 'Unlock Form Field',
      id: 'read_only_false',
      iconCss: 'e-icons e-unlock',
    },
  ];

  /* ---------------------------------- */
  /* Document Load */
  /* ---------------------------------- */
  documentLoad(): void {
    this.pdfviewerObj.addCustomMenu(this.menuItems, false);
  }

  /* ---------------------------------- */
  /* Context Menu Select */
  /* ---------------------------------- */
  customContextMenuSelect(args: any): void {
    switch (args.id) {
      case 'search_in_google':
        this.searchInGoogle();
        break;
      case 'lock_annotation':
        this.lockAnnotation();
        break;
      case 'unlock_annotation':
        this.unlockAnnotation();
        break;
      case 'read_only_true':
        this.setFormFieldReadOnly(true);
        break;
      case 'read_only_false':
        this.setFormFieldReadOnly(false);
        break;
    }
    args.cancel = false;
  }

  /* ---------------------------------- */
  /* Google Search */
  /* ---------------------------------- */
  searchInGoogle(): void {
    const textModule = this.pdfviewerObj.textSelectionModule;
    if (textModule?.isTextSelection) {
      textModule.selectionRangeArray.forEach((range: any) => {
        if (/\S/.test(range.textContent)) {
          window.open(`https://www.google.com/search?q=${range.textContent}`);
        }
      });
    }
  }

  /* ---------------------------------- */
  /* Lock Annotation */
  /* ---------------------------------- */
  lockAnnotation(): void {
    const selected = this.pdfviewerObj.selectedItems.annotations[0];
    if (!selected) return;

    const annotation = this.pdfviewerObj.annotationCollection.find(
      (a: any) => a.uniqueKey === selected.id
    );

    if (annotation) {
      const settings = annotation.annotationSettings as any;
      settings.isLock = true;
      annotation.isCommentLock = true;
      this.pdfviewerObj.annotation.editAnnotation(annotation);
    }
  }

  /* ---------------------------------- */
  /* Unlock Annotation */
  /* ---------------------------------- */
  unlockAnnotation(): void {
    const selected = this.pdfviewerObj.selectedItems.annotations[0];
    if (!selected) return;

    const annotation = this.pdfviewerObj.annotationCollection.find(
      (a: any) => a.uniqueKey === selected.id
    );

    if (annotation) {
      const settings = annotation.annotationSettings as any;
      settings.isLock = false;
      annotation.isCommentLock = false;
      this.pdfviewerObj.annotation.editAnnotation(annotation);
    }
  }

  /* ---------------------------------- */
  /* Form Field Read-only Handling */
  /* ---------------------------------- */
  setFormFieldReadOnly(isReadOnly: boolean): void {
    const fields = this.pdfviewerObj.selectedItems.formFields;
    fields.forEach((field: any) => {
      this.pdfviewerObj.formDesignerModule.updateFormField(field, {
        isReadOnly: isReadOnly,
      } as any);
    });
  }

  /* ---------------------------------- */
  /* Context Menu Visibility */
  /* ---------------------------------- */
  customContextMenuBeforeOpen(args: any): void {
    args.ids.forEach((id: string) => {
      const menuItem = document.getElementById(id);
      if (!menuItem) return;

      menuItem.style.display = 'none';

      if (
        id === 'search_in_google' &&
        this.pdfviewerObj.textSelectionModule?.isTextSelection
      ) {
        menuItem.style.display = 'block';
      }

      if (
        (id === 'lock_annotation' || id === 'unlock_annotation') &&
        this.pdfviewerObj.selectedItems.annotations.length
      ) {
        const selected = this.pdfviewerObj.selectedItems.annotations[0];
        const isLocked = (selected.annotationSettings as any)?.isLock;
        menuItem.style.display =
          (id === 'lock_annotation' && !isLocked) ||
          (id === 'unlock_annotation' && isLocked)
            ? 'block'
            : 'none';
      }

      if (
        (id === 'read_only_true' || id === 'read_only_false') &&
        this.pdfviewerObj.selectedItems.formFields.length
      ) {
        const field = this.pdfviewerObj.selectedItems.formFields[0];
        const readonly = field.isReadonly;
        menuItem.style.display =
          (id === 'read_only_true' && !readonly) ||
          (id === 'read_only_false' && readonly)
            ? 'block'
            : 'none';
      }
    });
  }
}

Handle Click Events for Custom Menu Items

The customContextMenuSelect() method defines actions for custom menu items. The implementation is included in the Angular component above in the customContextMenuSelect() method.

Dynamic Context Menu Customization

The customContextMenuBeforeOpen() event allows for dynamic showing or hiding of items based on selection or document state. The implementation is included in the Angular component above in the customContextMenuBeforeOpen() method.

Disable the Context Menu Entirely

The context menu in the PDF Viewer can be fully disabled by setting the contextMenuOption property to None.

import { Component, ViewChild } from '@angular/core';
import {
  PdfViewerComponent,PdfViewerModule,
  ToolbarService,NavigationService,
  LinkAnnotationService,BookmarkViewService,
  ThumbnailViewService,PrintService,
  TextSelectionService,TextSearchService,
  AnnotationService,FormDesignerService,
  FormFieldsService,
} from '@syncfusion/ej2-angular-pdfviewer';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [PdfViewerModule],
  providers: [
    ToolbarService,NavigationService,
    LinkAnnotationService,BookmarkViewService,
    ThumbnailViewService,PrintService,
    TextSelectionService,TextSearchService,
    AnnotationService,FormDesignerService,
    FormFieldsService,
  ],
  template: `
    <ejs-pdfviewer
      id="pdfViewer"
      [documentPath]="document"
      [resourceUrl]="resource"
      [contextMenuOption]="contextMenuOption"
      style="height: 100%; width: 100%; display: block"
    >
    </ejs-pdfviewer>
  `,
})
export class AppComponent {
  public document: string =
    'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf';

  public resource: string =
    'https://cdn.syncfusion.com/ej2/32.2.3/dist/ej2-pdfviewer-lib';

  public contextMenuOption: string = 'None';
}

NOTE

The context menu customization works with standalone and non-standalone Angular components, providing flexible integration options for your application.