Record and form events provide powerful hooks for processing data operations and user interactions with your application's forms.
Record Events
Record events fire during data operations — when records are created, updated, or deleted.
Supported Record Events
| Event | Description |
|---|---|
record:create | Fires globally whenever any record is created. |
record:create:view_key | Fires only when a record is created through a specific view. |
record:delete | Fires globally whenever any record is deleted. |
record:delete:view_key | Fires only when a record is deleted through a specific view. |
record:update | Fires globally whenever any record is updated. |
record:update:view_key | Fires only when a record is updated through a specific view. |
record:update-inline | Fires globally whenever any record is updated inline (e.g., in a table). |
record:update-inline:view_key | Fires only when a record is updated inline through a specific view. |
Knack.ready().then(async () => {
// Listen for record creation
Knack.on('record:create', ({ record, viewKey }) => {
console.log('Record created:', record);
console.log('In view:', viewKey);
if (record.field_priority === 'High') {
console.log('High priority record created, sending notifications');
sendNotificationToManagers(record);
}
});
// Listen for record updates
Knack.on('record:update', ({ record, viewKey }) => {
console.log('Record updated:', record);
processRecordUpdate(record, viewKey);
});
// Listen for record deletion
Knack.on('record:delete', ({ record, viewKey }) => {
console.log('Record deleted:', record);
});
});Record Event Use Cases
Record events are perfect for:
- Triggering workflows based on data changes
- Sending notifications when important records are created
- Updating related data in other systems
- Logging data operations for audit trails
- Implementing business rules and validations
// Example: Customer onboarding workflow
Knack.on('record:create', ({ record, viewKey }) => {
if (viewKey === 'view_customer_form' && record.field_customer_type === 'Enterprise') {
console.log('New enterprise customer created:', record.field_company_name);
triggerEnterpriseOnboarding(record);
assignEnterpriseAccountManager(record);
sendEnterpriseWelcomeEmail(record.field_email);
}
});Form Events
Forms in Next-Gen applications can be accessed and manipulated via custom JavaScript. When a form is rendered on a page, it is automatically registered and can be retrieved using its viewKey.
Supported Form Events
| Event | Callback Parameters | Description |
|---|---|---|
form:submit | { record, viewKey, isEdit } | Fires globally whenever any form is submitted. |
form:submit:view_key | { record, viewKey, isEdit } | Fires only when a specific form view is submitted. |
form:field-change:view_key | { viewKey, fieldKey, value, formData } | Fires the moment a user changes any field value on a specific form view. |
Form Access
Knack.page.getForm(viewKey)
Knack.page.getForm(viewKey)The entry point for all form interactions. Returns a form instance that provides access to all form methods described below.
| Parameter | Type | Description |
|---|---|---|
viewKey | string | The key of the form view (e.g., 'view_123'). |
Returns: A form instance object, or null if the form is not found.
Knack.ready().then(() => {
const form = Knack.page.getForm('view_123');
if (form) {
console.log('Form instance retrieved:', form);
}
});Form Methods
form.getValues(fieldKey)
form.getValues(fieldKey)Retrieves the current values from the form. Returns all values at once, or the value of a single field.
| Parameter | Type | Required | Description |
|---|---|---|---|
fieldKey | string | No | If provided, returns the value of only that field. If omitted, returns all form values. |
Returns: An object with all form values, or the value of a single field.
// Get all form values
const allValues = form.getValues();
console.log('All form values:', allValues);
// Get the value of a specific field
const name = form.getValues('field_1');
console.log('Name:', name);Form Submit Example
Knack.ready().then(async () => {
Knack.on('form:submit', ({ record, viewKey, isEdit }) => {
console.log('Form submitted:', record);
console.log('Is edit form:', isEdit);
if (record.field_email && !record.field_email.includes('@company.com')) {
console.log('External email detected:', record.field_email);
triggerExternalUserWorkflow(record);
}
});
});Form-Specific Submit Events
// Contact form
Knack.on('form:submit:view_456', ({ record, isEdit }) => {
if (!isEdit) {
console.log('New contact created:', record.field_name);
triggerNewContactWorkflow(record);
} else {
console.log('Contact updated:', record.field_name);
triggerContactUpdateWorkflow(record);
}
});
// Order form
Knack.on('form:submit:view_789', ({ record }) => {
const orderTotal = Number(record.field_total) || 0;
if (orderTotal > 1000) {
triggerLargeOrderApproval(record);
}
checkInventoryLevels(record.field_items);
});
Replacefield_name,field_total, andfield_itemswith your actual field keys (e.g.,field_123) from your app.
Real-Time Form Field Control
These APIs let you respond to field changes as they happen — displaying contextual messages, validating input, and controlling whether the form can be submitted. All methods are async and should be called with await.
Field Change Event
form:field-change:view_key
form:field-change:view_keyFires the moment a user changes any field value on the specified form view.
| Callback Parameter | Type | Description |
|---|---|---|
viewKey | string | The key of the form view where the change occurred. |
fieldKey | string | The key of the field that was changed. |
value | any | The new value of the changed field. |
formData | object | An object containing the current values of all fields in the form. |
Knack.ready().then(async () => {
Knack.on('form:field-change:view_123', async ({ viewKey, fieldKey, value, formData }) => {
console.log('Field changed:', fieldKey);
console.log('New value:', value);
console.log('All form data:', formData);
});
});View-Level Field Control Methods
These methods are called on Knack.view(viewKey) and allow you to control field messages and the submit button state in real time.
| Method | Parameters | Returns | Description |
|---|---|---|---|
showFieldMessage(fieldKey, options) | fieldKey (string), options (object) | Promise<void> | Displays a contextual message near the specified field. |
clearFieldMessage(fieldKey) | fieldKey (string) | Promise<void> | Removes any active message displayed near the specified field. |
setSubmitEnabled(enabled) | enabled (boolean) | Promise<void> | Enables or disables the form's submit button. |
getData() | — | Promise<Object> | Retrieves the current client-side data records for the view (e.g., a DataTable on the same page). |
showFieldMessage(fieldKey, options)
showFieldMessage(fieldKey, options)Displays a visually distinct contextual message directly below the specified field. Messages are styled by type.
| Option | Type | Required | Values |
|---|---|---|---|
type | string | Yes | 'info', 'warning', 'error', 'success' |
message | string | Yes | The text to display. |
await Knack.view(viewKey).showFieldMessage('field_110', {
type: 'warning',
message: 'Product Already Added'
});clearFieldMessage(fieldKey)
clearFieldMessage(fieldKey)Removes any active message previously shown near the specified field.
await Knack.view(viewKey).clearFieldMessage('field_110');setSubmitEnabled(enabled)
setSubmitEnabled(enabled)Controls the state of the form's submit button. Pass false to prevent submission, true to allow it.
await Knack.view(viewKey).setSubmitEnabled(false);
await Knack.view(viewKey).setSubmitEnabled(true);getData()
getData()Retrieves the current client-side data records for a view. Useful for cross-referencing existing records during form validation.
const tableData = await Knack.view('view_216').getData();Real-Time Validation Example
The following example prevents duplicate product selection in a form. When a user selects a product from a dropdown (field_110), the script checks an existing DataTable view (view_216) on the same page. If the product already exists in the table, a warning message is shown near the field and the submit button is disabled. If the product is new, the message is cleared and submission is re-enabled.
Knack.ready().then(async () => {
Knack.on('form:field-change:view_215', async ({ viewKey, fieldKey, value, formData }) => {
if (fieldKey === 'field_110') { // Product dropdown
// Retrieve current records from the table view on the same page
const tableData = await Knack.view('view_216').getData();
// Check if the selected product already exists
const productExists = tableData.records.some(record =>
record.field_110 === value
);
if (productExists) {
// Show a warning near the field
await Knack.view(viewKey).showFieldMessage('field_110', {
type: 'warning',
message: 'Product Already Added'
});
// Prevent form submission
await Knack.view(viewKey).setSubmitEnabled(false);
} else {
// Clear the warning and allow submission
await Knack.view(viewKey).clearFieldMessage('field_110');
await Knack.view(viewKey).setSubmitEnabled(true);
}
}
});
});Filter Persistence via Filter Change Events
You can use filter change events to implement custom filter persistence in Next-Gen apps. This lets you save a user's active filters to the browser's localStorageand reapply those filters when the view renders again.
This uses the Knack.on()event listener pattern.
Supported viewsFilter change events are supported for Table, List, Map, and Calendar views.
Pivot Table filters do not currently emit this event.
Scope of this featureThis approach persists filter state per user, per view, per browser. It does not support cross-device or cross-browser persistence, shared or public filter bookmarks, bookmark management such as named saves or reordering, or persisting search terms and pagination settings.
How It Works
| Trigger | Behavior |
|---|---|
| Filter change | The filters:change event fires when filters change on a supported view. You can save the current filter state to localStorage using the key format knack_filter_USERID_{viewID}. |
| View render | The view:render:view_### event fires when the view renders. You can check localStorage for a saved filter state and apply it back to the view using Knack.view(viewKey).applyFilters(). |
Implementation
Use Knack.on('filters:change', ...) to save filters when they change, and Knack.on('view:render:view_###', ...)to restore saved filters when the view renders.
Replace view_123with the key of the view where filters should be restored.
Knack.ready().then(() => {
const VIEW_KEY = 'view_123';
// Save filters when they change
Knack.on('filters:change', ({ viewKey, filters }) => {
Knack.getUser().then(user => {
const userId = user?.id ?? 'guest';
const storageKey = `knack_filter_$USERID_${viewKey}`;
if (filters?.rules?.length) {
localStorage.setItem(storageKey, JSON.stringify(filters));
} else {
localStorage.removeItem(storageKey);
}
});
});
// Restore filters when the view renders
Knack.on(`view:render:${VIEW_KEY}`, ({ viewKey }) => {
Knack.getUser().then(user => {
const userId = user?.id ?? 'guest';
const storageKey = `knack_filter_$USERID_${viewKey}`;
const savedFilters = localStorage.getItem(storageKey);
if (savedFilters) {
try {
Knack.view(viewKey).applyFilters(JSON.parse(savedFilters));
} catch {
localStorage.removeItem(storageKey);
}
}
});
});
});Parameters
| Parameter | Type | Description |
|---|---|---|
viewKey | string | The key of the view where the filter change or view render event occurred. |
filters | object | The current filter state as a Knack-exposed JSON object. Contains the active filter rules for the view at the time of the change. |
Use Cases
- Preserving a user's active filters when they navigate away and return to a page
- Reducing repetitive filter setup for users who work with the same filtered data set daily
- Pairing with preset filters to give users a personalized starting state
Best Practices
Separate Business Logic from Presentation
// ✅ Good — business logic only
Knack.on('form:submit', ({ record, viewKey }) => {
if (record.field_priority === 'High') {
sendUrgentNotification(record);
}
});
// ⚠️ Caution — direct DOM manipulation can conflict with Next-Gen rendering
Knack.on('form:submit', ({ data }) => {
document.querySelector('.status').innerHTML = 'Processing...';
document.querySelector('.button').style.backgroundColor = 'green';
});
Updating or creating a record via a view-based API request does not add or update a value if the field is not included in the form.

