The Custom Field Manager is a JavaScript API within LearningSuite that can be used to read, update and observe Custom Fields. This guide is intended exclusively for IT-savvy users (e.g. web developers)!
It is particularly suitable for custom integrations, AI Agents and dynamic extensions within elements such as Hubs or Lesson content.
Availability
The Custom Field Manager is globally available via:
window.customFields
or in short:
customFields
Typical use cases
Reading system or profile information (e.g. username, contact details)
Writing values to Custom Fields (stored server-side)
Reactive UI logic when fields change
Working with multiple profiles per Card (e.g. locations, roles, devices)
Initialization
As Custom Fields are loaded asynchronously, you must wait for initialization before using them:
await customFields.waitForInitialization();
This is recommended for all scripts that are executed on page load or early in the lifecycle.
Reading field values
Reading a single field
await customFields.waitForInitialization(); const firstName = customFields.getFieldValue('system.user.firstName');With typing:
const firstName = customFields.getFieldValue<string>('system.user.firstName');Reading all field values
const allValues = customFields.getFieldValues();
Ideal for debugging or exploratory development.
Setting field values
Field values can be updated server-side:
await customFields.setFieldValue( 'profile.contact.phone', '+43 660 1234567' );
Important notes:
Updates are performed asynchronously
A patch update is performed (other fields are not overwritten)
A throttle is active server-side to prevent too many requests
Reactive use (Observer)
Observing a single field
const unsubscribe = customFields.observeField( 'system.user.firstName', (value) => { console.log('New value:', value); } ); // Important: clean up later unsubscribe();Suitable for UI bindings or validation logic.
Observing all fields
const unsubscribe = customFields.observeFields((values) => { console.log('At least one field has changed', values); }); unsubscribe();Working with profiles
A Card can contain multiple profiles (e.g. locations, user roles).
Switching profiles
customFields.switchProfile('card-123', 1);Adding a profile
const profileIndex = customFields.addProfile( 'card-123', 'Vienna branch' );
Deleting a profile
customFields.deleteProfile('card-123', 2);Profile-specific field access (optional)
These methods are intended for special cases and should only be used when the profile index is explicitly relevant.
Reading
const email = customFields.getFieldValueForProfile( 'profile.contact.email', 0 );
Writing
await customFields.setFieldValueForProfile( 'profile.contact.email', '[email protected]', 0 );
Best practices
Always use
waitForInitialization()when your code is executed earlyAlways unsubscribe observers (
observeField,observeFields)Do not use
setFieldValuein unthrottled events (e.g.keyup)Only use profile-specific methods when truly necessary
For standard use cases, preferably use the LearningSuite Widgets
Example: Safe access with fallback
await customFields.waitForInitialization(); const firstName = customFields.getFieldValue('system.user.firstName') ?? 'Guest'; document.querySelector('#welcome').textContent = `Hello, ${firstName}!`;API overview (short version)
waitForInitialization()getFieldValue(key)setFieldValue(key, value)getFieldValues()observeField(key, callback)observeFields(callback)switchProfile(cardId, profileIndex)addProfile(cardId, name)deleteProfile(cardId, profileIndex)
Long version
// the custom Field manager is accessible via the following code:
//window.customFields: CustomFieldManagerType
/**
* @type {CustomFieldManagerType}
*/
window.customFields
//OR directly
/**
* @type {CustomFieldManagerType}
*/
customFields
// Usage:
customFields.getFieldValue('system.user.firstName')
//Custom Field Manager Type:
interface CustomFieldManagerType {
/**
* Waits for the CustomFieldManager to be initialized.
* This is useful to ensure that the data is loaded before accessing field values.
* @returns A promise that resolves when the CustomFieldManager is initialized.
*/
waitForInitialization(): Promise<void>;
/** Returns all field values for the current profile selection. */
getFieldValues(): Record<string, any>;
/**
* Gets the value of a field for the current profile selection.
* @param key The key of the field.
*/
getFieldValue<T>(key: string): T | undefined;
/**
* Sets the value of a field for the current profile selection.
* This operation will perform an asynchronous update to the server.
* It will execute a patch update to avoid overwriting other fields.
*
* We have set a throttle on the server updates to avoid too many requests in a short time.
* @param key The key of the field.
* @param value The value to set.
*/
setFieldValue<T>(key: string, value: T): Promise<void>;
/**
* Gets the value of a field for a specific profile index.
* This method is optional and should only be used when necessary.
* It's not recommended to use this method in most cases.
* @param key
* @param profileIndex
*/
getFieldValueForProfile<T>(key: string, profileIndex: number): T | undefined;
/**
* Sets the value of a field for a specific profile index.
* This method is optional and should only be used when necessary.
* It's not recommended to use this method in most cases.
* @param key
* @param value
* @param profileIndex
*/
setFieldValueForProfile<T>(key: string, value: T, profileIndex: number): Promise<void>;
/**
* Observes all field values. The callback is called whenever any field value changes.
* Please ensure to call the returned unsubscribe function when no longer needed to avoid memory leaks.
* @param callback
*/
observeFields(callback: Callback<Record<string, any>>): UnsubFn;
/**
* Observes a specific field value. The callback is called whenever the field value changes.
* Please ensure to call the returned unsubscribe function when no longer needed to avoid memory leaks.
* @param key
* @param callback
*/
observeField<T>(key: string, callback: (value: T | undefined) => void): () => void;
/**
* Switches the current profile for a given card.
* This is usefull if you want to integrate profile switching in your own UI.
* Otherwise just use the provided Widgets from LearningSuite directly.
* @param cardId
* @param profileIndex
*/
switchProfile(cardId: string, profileIndex: number): void;
/**
* Adds a new profile for a given card.
* This is usefull if you want to integrate profile creation in your own UI.
* Otherwise just use the provided Widgets from LearningSuite directly.
* @param cardId
* @param name
*/
addProfile(cardId: string, name: string): number;
/**
* Deletes a profile for a given card.
* This is usefull if you want to integrate profile deletion in your own UI.
* Otherwise just use the provided Widgets from LearningSuite directly.
* @param cardId
* @param profileIndex
*/
deleteProfile(cardId: string, profileIndex: number): void;
}
