Event System Overview

Next-Gen Knack features a comprehensive event system built around the Knack.on() method, providing structured data and modern JavaScript patterns for business logic.

What You'll Learn

You'll learn about the event-driven architecture that powers Next-Gen Knack applications. Learn how to listen for page renders, view updates, form submissions, and record changes to trigger business logic at precisely the right moments. This approach separates presentation from logic, creating more maintainable and powerful applications.

👍

Every JavaScript customization must begin with the Knack.ready() method. This ensures the Knack object is fully initialized and the React application has finished mounting before your custom code executes.

Event Categories

The Knack event system is organized into several main categories:

  • Page Events - Triggered when pages are rendered or navigated
  • View Events - Fired when specific views are displayed
  • Record Events - Occur during data operations (create, update, delete)
  • Form Events - Handle form interactions and submissions

Event Naming Conventions

Next-Gen events use a hierarchical naming system that allows both broad and specific event listening:

General Event Patterns

  • page:render - Any page renders
  • view:render - Any element renders
  • form:submit - Any form submits
  • record:create - Any record is created

Specific Event Patterns

  • page:render:scene_123 - Specific page renders
  • view:render:view_456 - Specific element renders
  • form:submit:view_789 - Specific form submits

Type-Specific Event Patterns

  • view:render:form - Any form view renders
  • view:render:table - Any table view render
  • view:render:details - Any details view renders
  • view:render:list - Any list view renders

Event Data Structures

Each event provides a consistent data object with relevant information:

Page Events

Knack.on('page:render', ({ pageKey }) => {
  console.log('Event data:', { pageKey });
  // pageKey: string (e.g., 'scene_123')
});

View Events

JavaScript
Knack.on('view:render', ({ viewKey }) => {
  console.log('View rendered:', {
    viewKey,   // e.g., 'view_456'
  });
});

Form Events

Knack.on('form:submit', ({ record, viewKey, isEdit }) => {
  console.log('Event data:', { record, viewKey, isEdit });
  // record: object (form field values after submission)
  // viewKey: string (e.g., 'view_456')
  // isEdit: boolean (true for edit forms)
});

Form Focus

Overview
The Knack form API allows you to programmatically set field values and focus form inputs using custom JavaScript code. This is useful for automating form filling, creating custom user experiences, and building dynamic form interactions.

Basic Setup

Knack.ready().then(() => {
  Knack.on('view:render', ({ viewKey }) => {
    const form = Knack.page.getForm(viewKey);
    if (form) {
      // Your setValue and focus calls here
    } else {
      console.log('Form not found for view:', viewKey);
    }
  });
});

API Methods

form.setValue(fieldKey, value) - Sets the value of a form field

form.focus(fieldKey) - Focuses a form field or field component

Field Types Reference
Text Fields

Short Text & Paragraph Text

form.setValue('field_43', "Example Text");
form.focus('field_43');
form.setValue('field_45', "Multi-line paragraph text");
form.focus('field_45');

Rich Text

form.setValue('field_46', "<b>HTML formatted text</b>");
form.focus('field_46');

Numeric Fields

// Number field
form.setValue('field_47', 123);
form.focus('field_47');

// Currency field  
form.setValue('field_48', 99.99);
form.focus('field_48');

// Rating field
form.setValue('field_71', 4); // Rating 1-5
form.focus('field_71');

Choice Fields

Single Selection

form.setValue('field_50', "Option Name");
form.focus('field_50');

Multiple Selection  

form.setValue('field_51', ["Option 1", "Option 2"]);
form.focus('field_51');

Checkboxes

form.setValue('field_52', ["Selected Option"]);
form.focus('field_52');

Boolean Fields

// Works for dropdown, checkbox, and radio button boolean fields
form.setValue('field_54', "Yes"); // or "No"
form.focus('field_54');

Date & Time Fields
Basic Date/Time

form.setValue('field_57', {
  date: new Date('2023-01-01T22:00:00'),
  all_day: false
});
// Focus the date input
form.focus('field_57.date');

Date Ranges with Recurring Events

form.setValue('field_59', {
  date: {
    from: new Date('2023-01-01T14:53:00')
  },
  repeat: {
    frequency: 'weekly',
    interval: 2, 
    weekDays: ['SA', 'SU'],
    endson: 'never'
  }
});
form.focus('field_59.date');

Name Fields

form.setValue('field_64', { 
  first: "John", 
  middle: "Michael", 
  last: "Doe" 
});
// Focus individual name components
form.focus('field_64.first');
form.focus('field_64.middle');
form.focus('field_64.last');

Contact Fields
Email

form.setValue('field_65.email', "[email protected]");
form.focus('field_65.email');

Phone

form.setValue('field_68', "(555) 123-4567");
form.focus('field_68');

Link/URL

form.setValue('field_69.url', "https://example.com");
form.focus('field_69.url');

Address Fields
Standard Address

// Set each address component
form.setValue('field_66.street', "123 Main Street");
form.setValue('field_66.street2', "Suite 100");
form.setValue('field_66.city', "San Francisco");
form.setValue('field_66.state', "CA");
form.setValue('field_66.zip', "94102");

// Focus individual address components  
form.focus('field_66.street');
form.focus('field_66.street2');
form.focus('field_66.city');
form.focus('field_66.state');
form.focus('field_66.zip');

Geographic Coordinates

form.setValue('field_67.latitude', "37.7749");
form.setValue('field_67.longitude', "-122.4194");
form.focus('field_67.latitude');
form.focus('field_67.longitude');

Media Fields
Image (URL)

form.setValue('field_63', "https://example.com/image.jpg");
form.focus('field_63');

Limitations & Special Cases
DateTime Time Input Focus Limitation ⚠️
Issue: DateTimePicker components render separate date and time inputs, but the form system only recognizes the main field registration.


// ✅ This works - focuses date input
  
form.focus('field_57.date'); 
// ❌ This does NOT work - time input focus not supported
form.focus('field_57.time');
Reason: The DateTimePicker registers as a single field with react-hook-form (field_57.date) even though it renders two DOM inputs. The time input exists in the DOM but is not registered as a separate field, so form.focus('field_57.time') will fail.
  
Unsupported Field Types
File Upload Fields ❌

Cannot be set programmatically

Files upload immediately upon selection before JavaScript can intercept

Only file IDs are accessible after upload completes

Signature Fields ❌  

Cannot be set programmatically

Requires user interaction for drawing/signing

Read-Only System Fields ❌

Record ID, Created On, Updated On, Created By, etc.

These fields are managed automatically by Knack

Error Handling

Knack.ready().then(() => {
  Knack.on('view:render', ({ viewKey }) => {
    const form = Knack.page.getForm(viewKey);
    if (!form) {
      console.warn('Form not found for view:', viewKey);
      return;
    }
    try {
      form.setValue('field_43', "Test Value");
      form.focus('field_43');
    } catch (error) {
      console.error('Error setting field value:', error);
    }
  });
});

Best Practices
1. Always Verify Form Exists

const form = Knack.page.getForm(viewKey);
if (!form) {
  console.warn('Form not available');
  return;
}

2. Use Descriptive Error Handling

try {
  form.setValue('field_43', value);
} catch (error) {
  console.error(`Failed to set field_43:`, error.message);
}

3. Test Field Access First

// Test if field exists before setting
if (form.fields['field_43']) {
  form.setValue('field_43', 'value');
}

4. Handle Async Operations

// For dynamic forms or conditional fields
setTimeout(() => {
  const form = Knack.page.getForm(viewKey);
  if (form) {
    form.setValue('field_43', 'delayed value');
  }
}, 100);

Record Events

Knack.on('record:create', ({ record, viewKey }) => {
  console.log('Record created:', record);
});

Knack.on('record:update', ({ record, viewKey }) => {
  console.log('Record updated:', record);
});

Knack.on('record:delete', ({ recordId, viewKey }) => {
  console.log('Record deleted:', recordId);
});

Record ID

Knack.getCurrentRecordId() is a function that returns the current page's record ID. It provides the same functionality as the commonly-used Knack.hash_id property in Classic apps, while following our standard naming conventions for Next-Gen custom code functions.

// Get the current record ID
const recordId = Knack.getCurrentRecordId();

Basic Event Syntax

All events follow the same pattern using Knack.on():

JavaScript
Knack.ready().then(async () => {
  // Listen for any event of this type
  Knack.on('event:type', (payload) => {
    console.log('Event triggered:', payload);
  });

  // Listen for a specific event
  Knack.on('event:type:specific_id', (payload) => {
    console.log('Specific event triggered:', payload);
  });
});

Event Data Structure

Each event provides structured data relevant to the event type:

JavaScript
Knack.on('view:render', ({ viewKey }) => {
  console.log('View rendered:', {
    viewKey,   // e.g., 'view_456'
  });
});

Best Practices

Use Events for Logic

Events should trigger business logic, data processing, and API calls

// ✅ Good - Business logic
Knack.on('record:create', ({ record, viewKey }) => {
  console.log('New record created:', record);
  
  if (record.field_priority === 'High') {
    console.log('High priority record, sending notifications');
    sendNotificationToManagers(record);
  }
});

// ⚠️ Use with caution - DOM manipulation
Knack.on('view:render', ({ viewKey }) => {
  document.querySelector('.kn-button').style.color = 'red';
});

Efficient Event Registration

Register events efficiently to avoid performance issues:

// ✅ Efficient - Single listener with conditional logic
Knack.on('view:render', ({ viewKey }) => {
  if (viewKey.includes('form')) {
    handleFormRender(viewKey);
  } else if (viewKey.includes('table')) {
    handleTableRender(viewKey);
  }
});

// ❌ Inefficient - Multiple similar listeners
for (let i = 1; i <= 50; i++) {
  Knack.on(`view:render:view_${i}`, () => {
    console.log(`View ${i} rendered`);
  });
}

Error Handling in Events

Handle potential errors in event listeners:

Knack.on('form:submit', async ({ record, viewKey }) => {
  try {
    await processFormSubmission(record);
    console.log('Form processed successfully');
  } catch (error) {
    console.error('Form processing failed:', error);
    // Handle error appropriately
  }
});

Event Lifecycle

Events follow a predictable lifecycle that you can leverage:

  1. Application Load - Knack.ready() resolves
  2. Page Navigation - Page events fire
  3. View Rendering - View events fire in sequence
  4. User Interaction - Form and record events fire
  5. Data Changes - Record events fire with updated data

Understanding this lifecycle helps you place your business logic at the optimal points in your application's flow.