Skip to main content

Common Attribute API

Every controller returned by useAttribute(...) extends the base AttributeController, so the members below are available on all attribute controllers.

Getting the controller

import { useAttribute } from "@expivi/product-configurator";

const attribute = useAttribute(configurator, attributeCid);

End-to-end example

Read the current state, react to changes, then clean up:

import { useAttribute } from "@expivi/product-configurator";

const attribute = useAttribute(configurator, attributeCid);

console.log(attribute.data.identifier, attribute.isVisible);

const unsubscribe = attribute.subscribe(next => {
console.log("changed", next.configuredValue);
});

// later
unsubscribe();

Intercepting value changes

The package exports a small typed API for running an async check before a value change is applied. It lets you confirm or veto a change (for example, asking the shopper to confirm a costly upgrade before it is committed).

import type {
BeforeSetValueContext,
BeforeSetValueResult,
BeforeSetValueHandler,
} from "@expivi/product-configurator";

The types are:

type BeforeSetValueContext = {
attributeCid: string;
valueCid: string;
};

type BeforeSetValueResult = {
confirmed: boolean;
};

type BeforeSetValueHandler = (context: BeforeSetValueContext) => Promise<BeforeSetValueResult>;

A handler receives the attribute/value cid pair being set and resolves to { confirmed: true } to allow the change or { confirmed: false } to veto it.

const attribute = useAttribute(configurator, attributeCid);

attribute.registerBeforeSetValue(async ({ attributeCid, valueCid }) => {
const confirmed = await showConfirmDialog(
`Apply ${valueCid} to ${attributeCid}?`,
);

return { confirmed };
});

// Remove the intercept when you no longer need it
attribute.unregisterBeforeSetValue();

Handlers are stored per attribute cid on the configurator, so registering again replaces the previous handler for that attribute.

API

messagesController

Reference to the shared MessagesController for this configurator.

registerBeforeSetValue(handler)

Registers a BeforeSetValueHandler that runs before a value is set on this attribute. See Intercepting value changes.

unregisterBeforeSetValue()

Removes the before-set-value handler for this attribute.

registerBeforeIncrementValue(handler)

Registers a BeforeSetValueHandler used by quantity-oriented flows such as bundle quantity increments.

unregisterBeforeIncrementValue()

Removes the before-increment handler for this attribute.

locale

Returns the configurator's current locale (configurator.getLocale()).

data

Returns the current typed attribute object. Throws if the attribute no longer exists.

isPristine

Returns true when the attribute has not been modified by user interaction.

isDirty

Returns the inverse of isPristine.

markAsDirty()

Marks the attribute as user-modified. Call this from the UI layer on user interaction.

markAsPristine()

Clears the dirty flag, typically after saving or resetting a configuration.

values

Returns value entities from the current value page, preserving page order and active filtering.

allValues

Returns every value entity for this attribute, regardless of the active page.

messages

Returns messages scoped to this attribute's identifier.

isVisible

Returns true when the attribute is not disallowed by rules.

isDisallowed

Returns the raw disallowed state.

setDisallowed(disallowed)

Updates the disallowed state and runs the controller-specific disallow hook when the attribute becomes disallowed.

isStatic

Returns true for static-input attributes (no user selection).

isSingle

Returns true for single-select attributes.

isMultiple

Returns true for multi-select attributes.

isValuable

Returns true when the attribute stores value entities such as artwork, boolean, list, modifier, or product values.

exists

Returns true if the attribute still exists in the store.

clearDisallowedConfiguredValues()

Clears configured values that are no longer part of the visible value set (as filtered by rules) and runs the controller-specific disallowed-value handling. Returns true if any values were changed.

produceSetValue(value)

Low-level method that builds the next attribute state without committing it. In normal application code, call setValue(...) instead.

subscribe(callback)

Subscribes to changes for this specific attribute. Returns an unsubscribe function.

const unsubscribe = attribute.subscribe(next => console.log(next));
unsubscribe();

subscribeMessages(callback)

Subscribes to messages for this specific attribute. Returns an unsubscribe function.

setValue(value)

Builds the next state with produceSetValue(...) and writes it to the store only if the configured value actually changed.

getValueByIdentifier(identifier)

Finds a current-page value entity by identifier, or returns null.

getValueById(id)

Finds a value entity by persistent id, or returns null.

getValueByCid(cid)

Finds a value entity by runtime cid, or returns null.

getSelectableValueById(id)

Returns the value with the given id only if it is on the current page or already part of the configured value (an off-page selection); otherwise returns null.

reset()

Resets the attribute to its initial store state.

serialize()

Serializes the attribute into the Expivi configuration format.

parse(data)

Parses serialized attribute data into the current attribute.

loadValuePage(pageConfig)

Loads a value page for this attribute, merging the given config over the current value-page config. Returns the loaded value cids.

selectedCategoryIds

Returns the currently selected category ids from the value-page config.

selectedCategoryId

Convenience getter for the first selected category id, or null. Use selectedCategoryIds for the full list.

setCategoryFilter(input)

Sets the category filter and reloads the value page (combined with search and pagination). Accepts a single id (toggled on/off), an array of ids (replaces the selection), or null (clears it).

loadAvailableCategories()

Fetches the list of available Category items for this attribute.

Each subscribe(...) / subscribeMessages(...) call returns an unsubscribe function. Always call it when the controller is torn down to release the store subscription.