Skip to main content

Custom Fields Service

This article describes how you can use and access the Custom Fields Service for User Properties in LearningSuite.

V
Written by Valerie Larissa Feichtmair

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 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, 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;

}

Did this answer your question?