Skip to main content

Programmatic Configuration Control

Mimeeq provides several methods to control product configurations programmatically. Whether you're building a custom UI, integrating with external systems, implementing guided selling flows, or restoring saved configurations, this guide covers all the approaches available.

Overview of Methods

MethodUse CaseBest For
setConfigurationCodeApply complete configuration at onceLoading presets, restoring saved configs, bulk changes
markOptionByBlockNameAndOptionCodeChange single option by nameSimple integrations, guided flows
markOptionFull control with option objectsAdvanced integrations, custom UIs
markOptionModularChange options in modular scenesMulti-product scenes, applying to multiple instances

Setting Complete Configurations

setConfigurationCode

The most efficient way to apply multiple options at once. This method accepts a complete configuration code string and updates all options simultaneously.

// Apply a complete configuration
await mimeeqApp.actions.setConfigurationCode('Width-a1&Fabric-b3&Color-c2&Legs-a4');
tip

For details on how configuration codes are structured (block-option pairs, special widget encoding, character escaping), see the Configuration Code Structure documentation.

Common Use Cases

Loading a saved configuration:

// Restore from localStorage
const savedConfig = localStorage.getItem('lastConfiguration');
if (savedConfig) {
await mimeeqApp.actions.setConfigurationCode(savedConfig);
}

Loading from URL parameters:

const urlParams = new URLSearchParams(window.location.search);
const configCode = urlParams.get('config');

if (configCode) {
await mimeeqApp.actions.setConfigurationCode(configCode);
}

Implementing "Popular Configurations":

const popularConfigs = {
'Classic Oak': 'Material-a1&Finish-b2&Size-c3',
'Modern White': 'Material-a5&Finish-b1&Size-c2',
'Industrial': 'Material-a3&Finish-b4&Size-c1',
};

function applyPopularConfig(name) {
const code = popularConfigs[name];
if (code) {
mimeeqApp.actions.setConfigurationCode(code);
}
}

Syncing configurations between products:

// Save current configuration when user leaves
window.mimeeqApp.observers.product.configurationCode.subscribe(({ newValue }) => {
if (newValue) {
sessionStorage.setItem('currentConfig', newValue);
}
});

// Apply to another product (if compatible)
document.addEventListener('mimeeq-3d-product-initialized', () => {
const savedConfig = sessionStorage.getItem('currentConfig');
if (savedConfig) {
mimeeqApp.actions.setConfigurationCode(savedConfig);
}
});

Getting the Current Configuration Code

// One-time read
const currentCode = window.mimeeqApp.observers.product.configurationCode.value;

// Subscribe to changes
window.mimeeqApp.observers.product.configurationCode.subscribe(({ newValue }) => {
console.log('Configuration changed:', newValue);
});

Changing Individual Options

markOptionByBlockNameAndOptionCode

The simplest way to change a single option using human-readable identifiers. This method automatically detects the widget type and handles the appropriate payload.

await mimeeqApp.actions.markOptionByBlockNameAndOptionCode(blockName, value);

Standard Options

For dropdown, swatch, radio, and other standard option widgets:

// Select by option code
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Fabric', 'LEATHER-BLACK');
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Frame Color', 'OAK');
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Size', 'LARGE');

Slider Widget

Set numeric values within the slider's defined range:

// Set slider to specific value
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Width', 150);
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Height', 80.5);

// Also accepts string numbers
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Depth', '45');

Text Input Widget

Set free-form text values:

await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Custom Label', 'John Doe');
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Engraving Text', 'EST. 2024');

Number Input Widget

Set numeric input values:

await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Quantity', 25);
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Custom Width', '120.5');

Color Picker Widget

Set custom colors using hex values:

await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Custom Color', '#3498db');
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Accent Color', '#FF5733');

Applying Multiple Options Sequentially

// Apply a predefined configuration step by step
async function applyConfiguration() {
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Fabric', '6550_PINK');
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Frame', 'GREY');
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Width', 120);
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Custom Label', 'My Chair');
}

// Or use a configuration array
const config = [
{ block: 'Fabric', value: '6550_PINK' },
{ block: 'Frame', value: 'GREY' },
{ block: 'Width', value: 120 },
];

for (const item of config) {
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode(item.block, item.value);
}

Advanced Control with markOption

For maximum control, use markOption directly. This is useful when you need to set complex payloads or already have block references from observers.

await mimeeqApp.actions.markOption(option, blockId, widgetType, setMatchingGroup, blockCode);

Modular Products: markOptionModular

For modular products (scenes with multiple product instances), use markOptionModular. It works like markOption but can apply changes to multiple instances simultaneously.

await mimeeqApp.actions.markOptionModular(
option, // Option object (same payloads as markOption)
blockId, // Block ID
widgetType, // Widget type
setMatchingGroup, // Apply to matching blocks
blockCode, // Block name/code
instanceIds // Optional: specific instance IDs to target
);

Targeting Instances

// Apply to currently selected instance(s) - omit instanceIds
await mimeeqApp.actions.markOptionModular(
{ metafields: { inputValue: 100 } },
block.id,
block.widgetType,
true,
block.blockName
);

// Apply to specific instances
await mimeeqApp.actions.markOptionModular(
{ colorSettings: { color: '#FF0000' } },
block.id,
block.widgetType,
true,
block.blockName,
['instance-123', 'instance-456'] // Only these instances
);

// Apply to all instances of a product type
const allChairInstances = Object.entries(productElements)
.filter(([_, el]) => el.productId === 'CHAIR-001')
.map(([instanceId]) => instanceId);

await mimeeqApp.actions.markOptionModular(
option,
block.id,
block.widgetType,
true,
block.blockName,
allChairInstances
);

Events

  • mimeeq-modular-select-option - Fired when option is applied to a single instance
  • mimeeq-modular-select-option-multiple - Fired when option is applied to multiple instances

Finding Block Information

function getBlockByName(blockName) {
const blocks = window.mimeeqApp.observers.product.optionSetsBlocks.value;
return blocks?.find(block => block.blockName === blockName);
}

const block = getBlockByName('Width');
// block.id, block.widgetType, block.blockName are now available

Widget-Specific Payloads

Slider

const block = getBlockByName('Width');

await mimeeqApp.actions.markOption(
{ metafields: { inputValue: 150 } },
block.id,
block.widgetType,
true,
block.blockName
);

Text Input

const block = getBlockByName('Custom Label');

await mimeeqApp.actions.markOption(
{ inputSettings: { value: 'My Custom Text' } },
block.id,
block.widgetType,
true,
block.blockName
);

Number Input

const block = getBlockByName('Quantity');

await mimeeqApp.actions.markOption(
{ inputSettings: { value: '50' } },
block.id,
block.widgetType,
true,
block.blockName
);

Color Picker

const block = getBlockByName('Custom Color');

await mimeeqApp.actions.markOption(
{ colorSettings: { color: '#3498db' } },
block.id,
block.widgetType,
true,
block.blockName
);

Image Widget

const block = getBlockByName('Custom Logo');

await mimeeqApp.actions.markOption(
{
imageSettings: {
image: 'uploaded-image-id',
uOffset: 0,
vOffset: 0,
scale: 1
}
},
block.id,
block.widgetType,
true,
block.blockName
);
const block = getBlockByName('T-Shirt Print');

await mimeeqApp.actions.markOption(
{
printOnDemandSettings: {
image: 'data:image/png;base64,...', // or uploaded URL
opaque: false,
metadata: {
left: 100,
top: 150,
scaleX: 1,
scaleY: 1,
angle: 0
}
}
},
block.id,
block.widgetType,
true,
block.blockName
);

Engrave

const block = getBlockByName('Personalization');

await mimeeqApp.actions.markOption(
{
text: ['Line 1', 'Line 2'],
settings: {
fontFamily: 'Arial',
fontSize: 24
}
},
block.id,
block.widgetType,
true,
block.blockName
);

Clearing Values

To clear a special widget value, pass null for the settings:

// Clear color
await mimeeqApp.actions.markOption(
{ colorSettings: null },
block.id,
block.widgetType,
true,
block.blockName
);

// Clear text input
await mimeeqApp.actions.markOption(
{ inputSettings: null },
block.id,
block.widgetType,
true,
block.blockName
);

Listening for Configuration Changes

Selected Options

window.mimeeqApp.observers.product.selectedOptions.subscribe(({ newValue }) => {
newValue.forEach(option => {
console.log(`Block: ${option.blockCode}, Value:`, option);

// Check for specific widget types
if (option.metafields?.inputValue !== undefined) {
console.log('Slider value:', option.metafields.inputValue);
}
if (option.colorSettings?.color) {
console.log('Color:', option.colorSettings.color);
}
if (option.inputSettings?.value) {
console.log('Input value:', option.inputSettings.value);
}
});
});

Configuration Code

window.mimeeqApp.observers.product.configurationCode.subscribe(({ newValue }) => {
console.log('New configuration code:', newValue);

// Auto-save to localStorage
localStorage.setItem('lastConfig', newValue);

// Update URL without reload
const url = new URL(window.location);
url.searchParams.set('config', newValue);
window.history.replaceState({}, '', url);
});

Pricing Updates

window.mimeeqApp.observers.pricing.prices.subscribe(({ newValue }) => {
if (newValue) {
document.getElementById('price').textContent =
`${newValue.currency} ${newValue.price.toFixed(2)}`;
}
});

Integration Examples

Custom Option Selector

// Build custom UI from block data
function buildCustomSelector(block) {
const container = document.createElement('div');
container.innerHTML = `<h3>${block.blockName}</h3>`;

const select = document.createElement('select');

block.options.forEach(option => {
const opt = document.createElement('option');
opt.value = option.code;
opt.textContent = option.name;
select.appendChild(opt);
});

select.addEventListener('change', (e) => {
mimeeqApp.actions.markOptionByBlockNameAndOptionCode(
block.blockName,
e.target.value
);
});

container.appendChild(select);
return container;
}

Guided Selling Flow

const steps = [
{ block: 'Category', question: 'What style do you prefer?' },
{ block: 'Material', question: 'Choose your material' },
{ block: 'Size', question: 'Select the size' },
{ block: 'Color', question: 'Pick a color' },
];

let currentStep = 0;

async function advanceStep(selectedValue) {
const step = steps[currentStep];
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode(step.block, selectedValue);

currentStep++;

if (currentStep < steps.length) {
showStep(steps[currentStep]);
} else {
showSummary();
}
}

External System Sync

// Sync with external PIM/ERP system
async function syncFromExternalSystem(externalConfig) {
const mapping = {
'ext_color_red': { block: 'Color', value: 'RED-001' },
'ext_color_blue': { block: 'Color', value: 'BLUE-002' },
'ext_size_small': { block: 'Size', value: 'S' },
'ext_size_large': { block: 'Size', value: 'L' },
};

for (const extKey of Object.keys(externalConfig)) {
const mapped = mapping[extKey];
if (mapped && externalConfig[extKey]) {
await mimeeqApp.actions.markOptionByBlockNameAndOptionCode(
mapped.block,
mapped.value
);
}
}
}

Best Practices

  1. Choose the right method:

    • Use setConfigurationCode for loading complete configurations
    • Use markOptionByBlockNameAndOptionCode for simple, one-off changes
    • Use markOption when you need full payload control
    • Use markOptionModular for modular scenes with multiple product instances
  2. Always await the promises - These methods are asynchronous:

    await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Width', 100);
    // Scene is now updated
  3. Handle errors gracefully:

    try {
    await mimeeqApp.actions.setConfigurationCode(savedConfig);
    } catch (error) {
    console.error('Failed to apply configuration:', error);
    // Fall back to defaults or show error message
    }
  4. Wait for initialization:

    document.addEventListener('mimeeq-3d-product-initialized', async () => {
    // Safe to call configuration methods now
    await mimeeqApp.actions.setConfigurationCode(initialConfig);
    }, { once: true });
  5. Validate before setting - Check block settings for constraints:

    const block = getBlockByName('Width');
    const { min, max } = block.sliderSettings || {};

    if (value >= min && value <= max) {
    await mimeeqApp.actions.markOptionByBlockNameAndOptionCode('Width', value);
    }
  6. Use setConfigurationCode for bulk changes - It's more efficient than multiple sequential calls when changing many options at once.


See Also