Skip to main content

Custom Fields Service

This article describes how to use and access the Custom Fields Service for User properties in LearningSuite.

Written by Alexander Knechtl

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 technically proficient 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 through:

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

Because Custom Fields are loaded asynchronously, you must wait for initialization before using them:

await customFields.waitForInitialization();

This is recommended for all scripts that run 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 asynchronous

  • A patch update is performed (other fields are not overwritten)

  • A throttle is active server-side to prevent too many requests


Reactive usage (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 runs early

  • Always unsubscribe observers (observeField, observeFields)

  • Do not use setFieldValue in unthrottled events (e.g., keyup)

  • Only use profile-specific methods when truly necessary

  • For standard use cases, use the LearningSuite Widgets instead


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 useful if you want to integrate profile switching into your own UI.

* Otherwise, 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 useful if you want to integrate profile creation into your own UI.

* Otherwise, 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 useful if you want to integrate profile deletion into your own UI.

* Otherwise, use the provided Widgets from LearningSuite directly.

* @param cardId

* @param profileIndex

*/

deleteProfile(cardId: string, profileIndex: number): void;

}

Did this answer your question?