Skip to main content
This document provides comprehensive information about all available events in JoyDoc and how to handle them.

Event Overview Table

Event NameEvent TriggersPurpose
onFocusWhen a field receives focusTrack field focus events for analytics, validation, or UI updates
onChangeWhen document data changes (field changes, field position changes, page changes, style changes, theme changes)Handle document updates, save changes, or sync with external systems
onBlurWhen a field loses focusTrack field blur events for validation or cleanup operations
onUploadAsyncWhen images or files are uploadedHandle file uploads to external storage or processing services
onCaptureAsyncWhen barcode is captured for table barcode cellsHandle camera capture functionality for barcode fields
onFileUploadAsyncWhen images or files are uploadedHandle field-specific file uploads with custom logic
onFileClickWhen a file in a field is clickedHandle file interactions like opening, previewing, or downloading
onFileDeleteWhen a file in a field is deletedHandle file deletion with cleanup or confirmation logic
onErrorWhen errors occur (schema validation, license, etc.)Handle and display errors to users

Event Details

onFocus

  • Triggered when: A field receives focus
  • Purpose: Track field focus events for analytics, validation, or UI updates
Event signature:
onFocus(params, e) => {
  // params contains field and document information
  // e is the native focus event
}

Parameters:
  • params: Object containing:
    • sdk: ‘js’
    • v: 1
    • type: ‘fieldPositionFocus’
    • _id: Document ID
    • identifier: Document identifier
    • fileId: File ID
    • pageId: Page ID
    • fieldId: Field ID
    • fieldIdentifier: Field identifier
    • fieldPositionId: Field position ID
    • fieldRowId: Row ID (for table fields)
    • fieldColumnId: Column ID (for table fields)
    • fieldColumnIdentifier: Column identifier (for table fields)
  • e: Native DOM focus event
Example:
<JoyDoc
  onFocus={(params, e) => {
    console.log('Field focused:', params.fieldId);
    // Call e.blur() to blur the field programmatically
    setTimeout(() => e.blur(), 3000);
  }}
/>

onChange

  • Triggered when: Document data changes
  • Purpose: Handle document updates, save changes, or sync with external systems
Triggered for:
  • Field changes - Field value or property updates
  • Field position changes - Field position, size, or display changes
  • Page changes - Page creation, updates, or deletion
  • Style changes - Theme and styling modifications
  • Theme changes - Toggle between themes
All these will cause changes to the document data Event signature:
onChange(changelog, doc) => {
  // changelog contains the changes made
  // doc contains the updated document
}

Parameters:
  • changelog: Array of change objects describing what was modified
  • doc: The updated document object
Example:
<JoyDoc
  onChange={(changelog, doc) => {
    console.log('Document changed:', changelog);
    console.log('Updated document:', doc);

    // Save changes to server
    saveDocument(doc);

    // Update local state
    setDocument(doc);
  }}
/>

onBlur

  • Triggered when: A field loses focus
  • Purpose: Track field blur events for validation or cleanup operations
Event signature:
onBlur(params, e) => {
  // params contains field and document information
  // e is the native blur event
}

Parameters:
  • params: Object containing:
    • sdk: ‘js’
    • v: 1
    • target: ‘fieldPositionBlur’
    • _id: Document ID
    • identifier: Document identifier
    • fileId: File ID
    • pageId: Page ID
    • fieldId: Field ID
    • fieldIdentifier: Field identifier
    • fieldPositionId: Field position ID
    • fieldRowId: Row ID (for table fields)
    • fieldColumnId: Column ID (for table fields)
    • fieldColumnIdentifier: Column identifier (for table fields)
  • e: Native DOM blur event
Example:
<JoyDoc
  onBlur={(params, e) => {
    console.log('Field blurred:', params.fieldId);

    // Validate field on blur
    validateField(params.fieldId);
  }}
/>

onUploadAsync

  • Triggered when: Images or files are uploaded
  • Purpose: Handle file uploads to external storage or processing services
Event signature:
onUploadAsync(params, fileUploads) => {
  // params contains upload context
  // fileUploads contains the files to upload
  // Must return array of file objects with url, fileName, filePath
}

Parameters:
  • params: Object containing upload context and document information
  • fileUploads: Array of file objects to upload
Return value: Array of file objects with:
  • url: Public URL of uploaded file
  • fileName: Name of the file
  • filePath: Path to the file
Example:
<JoyDoc
  onUploadAsync={async (params, fileUploads) => {
    console.log('Uploading files:', fileUploads);

    const uploadPromises = fileUploads.map(async (file) => {
      // Upload to your storage service
      const uploadedFile = await uploadToS3(file);

      return {
        url: uploadedFile.url,
        fileName: uploadedFile.fileName,
        filePath: uploadedFile.filePath
      };
    });

    return await Promise.all(uploadPromises);
  }}
/>

onCaptureAsync

  • Triggered when: Barcode is captured for table barcode cells
  • Purpose: Handle camera capture functionality for barcode fields
Event signature:
onCaptureAsync(params) => {
  // params contains capture context
  // Must return URL string of captured barcode
}

Parameters:
  • params: Object containing capture context
Return value: String data of the captured barcode Example:
<JoyDoc
  onCaptureAsync={async (params) => {
    console.log('Barcode capture initiated:', params);

    // Open camera and capture barcode
    const barcodeData = await captureBarcode();

    return barcodeData;
  }}
/>

onFileUploadAsync

  • Triggered when: Images or files are uploaded
  • Purpose: Handle field-specific file uploads with custom logic
Event signature:
onFileUploadAsync(params, fileUploads) => {
  // params contains field and upload context
  // fileUploads contains the files to upload
  // Must return array of file objects with url, fileName, filePath
}

Parameters:
  • params: Object containing field information and upload context
  • fileUploads: Array of file objects to upload
Return value: Array of file objects with:
  • url: Public URL of uploaded file
  • fileName: Name of the file
  • filePath: Path to the file
Example:
// Configure in fieldSettings
const fieldSettings = {
  field: {
    onFileUploadAsync: async (params, fileUploads) => {
      console.log('Field-specific upload:', params.fieldId, fileUploads);

      // Custom upload logic for this specific field
      const uploadPromises = fileUploads.map(async (file) => {
        const uploadedFile = await customUploadService(file, params.fieldId);

        return {
          url: uploadedFile.url,
          fileName: uploadedFile.fileName,
          filePath: uploadedFile.filePath
        };
      });

      return await Promise.all(uploadPromises);
    }
  }
};

<JoyDoc
  fieldSettings={fieldSettings}
/>

onFileClick

  • Triggered when: A file in a field is clicked
  • Purpose: Handle file interactions like opening, previewing, or downloading
Event signature:
onFileClick(params, urlObject) => {
  // params contains field and file context
  // urlObject contains file information
}

Parameters:
  • params: Object containing:
    • fileId: File ID
    • pageId: Page ID
    • fieldId: Field ID
    • fieldIdentifier: Field identifier
    • fieldPositionId: Field position ID
  • urlObject: Object containing:
    • url: File URL
    • fileName: File name
    • filePath: File path
Example:
// Configure in fieldSettings
const fieldSettings = {
  field: {
    onFileClick: (params, urlObject) => {
      console.log('File clicked:', urlObject);

      // Open file in new tab
      window.open(urlObject.url, '_blank');
    }
  }
};

<JoyDoc
  fieldSettings={fieldSettings}
/>

onFileDelete

  • Triggered when: A file in a field is deleted
  • Purpose: Handle file deletion with cleanup or confirmation logic
Event signature:
onFileDelete(params, urlObject) => {
  // params contains field and file context
  // urlObject contains file information
}

Parameters:
  • params: Object containing:
    • fileId: File ID
    • pageId: Page ID
    • fieldId: Field ID
    • fieldIdentifier: Field identifier
    • fieldPositionId: Field position ID
  • urlObject: Object containing:
    • url: File URL
    • fileName: File name
    • filePath: File path
Example:
// Configure in fieldSettings
const fieldSettings = {
  field: {
    onFileDelete: async (params, urlObject) => {
      console.log('File deleted:', urlObject);

      // Show confirmation dialog
      const confirmed = await showConfirmDialog(
        `Are you sure you want to delete ${urlObject.fileName}?`
      );

      if (confirmed) {
        // Delete from storage service
        await deleteFromStorage(urlObject.filePath);
      }

      return confirmed;
    }
  }
};

<JoyDoc
  fieldSettings={fieldSettings}
/>

onError

  • Triggered when: Errors occur (schema validation, license validation, etc.)
  • Purpose: Handle and display errors to users
Event signature:
onError(error) => {
  // error contains error information
}

Parameters:
  • error: Object containing error information:
    • For schema validation errors: Validation error details
    • For license errors: { code: 'LICENSE_VALIDATION_ERROR', message: string, type: 'license' }
Example:
<JoyDoc
  onError={(error) => {
    console.error('JoyDoc Error:', error);

    if (error.type === 'license') {
      showLicenseError(error.message);
    } else {
      showGenericError('An error occurred. Please try again.');
    }
  }}
/>

Complete Example

Here’s a complete example showing how to use all events:
import React, { useState } from 'react';
import JoyDoc from './JoyDoc';

const MyJoyDocApp = () => {
  const [doc, setDoc] = useState(initialDoc);
  const [isLoading, setIsLoading] = useState(false);

  const fieldSettings = {
    field: {
      onFileUploadAsync: async (params, fileUploads) => {
        setIsLoading(true);
        try {
          const uploadPromises = fileUploads.map(async (file) => {
            const uploadedFile = await uploadToS3(file);
            return {
              url: uploadedFile.url,
              fileName: uploadedFile.fileName,
              filePath: uploadedFile.filePath
            };
          });
          return await Promise.all(uploadPromises);
        } finally {
          setIsLoading(false);
        }
      },
      onFileClick: (params, urlObject) => {
        window.open(urlObject.url, '_blank');
      },
      onFileDelete: async (params, urlObject) => {
        const confirmed = await showConfirmDialog(
          `Delete ${urlObject.fileName}?`
        );
        if (confirmed) {
          await deleteFromStorage(urlObject.filePath);
        }
        return confirmed;
      }
    }
  };

  return (
    <div>
      {isLoading && <div>Uploading...</div>}

      <JoyDoc
        doc={doc}
        fieldSettings={fieldSettings}
        onChange={(changelog, updatedDoc) => {
          console.log('Document changed:', changelog);
          setDoc(updatedDoc);
          saveDocument(updatedDoc);
        }}
        onFocus={(params, e) => {
          console.log('Field focused:', params.fieldId);
          analytics.track('field_focused', params);
        }}
        onBlur={(params, e) => {
          console.log('Field blurred:', params.fieldId);
          validateField(params.fieldId);
        }}
        onUploadAsync={async (params, fileUploads) => {
          const uploadPromises = fileUploads.map(async (file) => {
            const uploadedFile = await uploadToS3(file);
            return {
              url: uploadedFile.url,
              fileName: uploadedFile.fileName,
              filePath: uploadedFile.filePath
            };
          });
          return await Promise.all(uploadPromises);
        }}
        onCaptureAsync={async (params) => {
          const barcodeData = await captureBarcode();
          return barcodeData;
        }}
        onError={(error) => {
          console.error('JoyDoc Error:', error);
          showError(error.message || 'An error occurred');
        }}
      />
    </div>
  );
};

Best Practices

  1. Async Event Handlers: Always use async/await for upload and capture events
  2. Error Handling: Implement proper error handling in all async event handlers
  3. Loading States: Show loading indicators during file uploads
  4. Validation: Use onBlur for field validation
  5. Analytics: Use onFocus and onChange for user interaction tracking
  6. File Management: Implement proper cleanup in onFileDelete handlers
  7. Performance: Debounce onChange events if needed for frequent updates