# Multi-object Configurator
The Multi-object Configurator allows you to configure not only one object but several objects in relation to each other.
# Enable multi-object configuration
To enable MoC:
- To enable multi-object configuration, you need to add and set the
moc
flag in theRubens settings
to true (see Rubens Admin documentation (opens new window)). When this flag is set, all configurators in the tenant are automatically multi-object configurators.
# Setup the catalog structure
To offer a set of products in the MoC, they must be stored in a catalog and tagged accordingly. To display products and divide them into categories, tags must be structured correctly.
Each tag can be used as root tag for the MoC. A sub-tag must be created for each category and each product that corresponds to this category must be tagged with it. The tag structure could then look like this:
1 moc_catalog (root-tag)
2 |
3 ├── Fences (sub-tag 1)
4 |
5 ├── Couch Tables (sub-tag 2)
6 |
7 ├── Outdoor Tables (sub-tag 3)
8 |
9 └── Sofas (sub-tag 4)
Also see the documentation on tags
By tagging the products in the catalog, you can add them to the scene by clicking the "Add Product" button.
NOTE: In order to use the tags in the MOC, the catalog that hosts these tags should be
Free
andAvailable for public
. You can set these flags inside the catalog detail page in the Admin interface. More info can be found here documentation on create catalog
# Define custom materials
You can customize which materials are used to color:
- walls
- floors
- colorable doors, windows and objects (be aware that not every door or window or object is colorable)
First of all you have to create a root tag for the materials you want to use in Rubens Admin. The tag structure is exactly the same as for the catalog. For more details see the section above (setup catalog). The only difference is, that you tag materials instead of items. Another important difference is, that additionally to the visibility rights describted in the catalog section, the materials need to belong to the tenant that instantiates the Rubens Room Designer. Normally that's not a problem when you setup the materials in Rubens Admin inside your tenant. But make sure that you use the correct configurator ID. Otherwise the materials won't be visible.
If you have setup the tag, then you just need to set the options correctly (either via query params, configurator settings or init-options).
You can set different tags for the different things you want to color:
- walls:
wallMaterialRootTag
- floors:
floorMaterialRootTag
- doors:
doorMaterialRootTag
- windows:
windowMaterialRootTag
- objects:
objectMaterialRootTag
If you do not want to use a separate tag for everything you can just use materialRootTag
and this is applied to everything. If no tag is specified Rubens Room Designer falls back to standard RAL colors. If you want to use a specific materialRootTag
for everything but for the walls you want to use the RAL colors, you would specify it the following way:
materialRootTag=my_custom_material_root_tag_id&wallMaterialRootTag=__SAMPLE__MATERIALS__
The phrase __SAMPLE__MATERIALS__
indicates to use the fallback materials.
# Integration
To embed or integrate the MoC, the following parameters must be included in the URL
- configuratorId: The configurator ID of the tenant where you enabled the MoC
- catalogRootTag: The tag of the catalog where you stored the products you want to show in the MoC, Alternatively you could also set the root-tag ID as catalogRootTag in your Rubens settings.
- moc: Enable the MoC feature in the Rubens settings or via integration.
Also see integration documentation: https://docs.roomle.com/web/embedding/integration.html (opens new window)
# Embedding:
If you are using the embedding library you have to call createPlanner
instead of create
or createConfigurator
to load
a MoC scene.
# Start scenarios for embedding the configurator into your website
Overall there are 2 possibilities to start with:
- An already existing plan -> Example (opens new window)
- An item (static item or configuration) -> Example (opens new window)
You can pass the item id
or plan id
using the id
parameter.
# Relevant parameters
startInDetail Documentation (opens new window)
- The
startInDetail
parameter controls in the user will start in configurator or in planner mode for a given item id. - Example for
startInDetail=true
(opens new window) -> User starts in the configurator - Example for
startInDetail=false
(opens new window) -> User start in the planner/scene
state.mode Documentation (opens new window)
- The
mode
parameter defines the initial mode of the planner. - Example for
state.mode=room
(opens new window) -> User starts in the room/drawing mode - Example for
state.mode=catalog
(opens new window) -> User starts in the catalog mode- Important: A
catalogRootTag
(see above) visible to the currentconfiguratorId
has to be provided
- Important: A
# Listen to onSaveDraft
To be informed when an action to save the draft is initiated, whether by the user inside the iframe or triggered externally, you should utilize the onSaveDraft
callback.
const planner = await RoomleConfiguratorApi.createPlanner(
'demoConfigurator',
document.getElementById('planner-container'),
options,
);
planner.ui.callbacks.onSaveDraft = (id, image, url, { type, payload }) => {
console.log(id, image, url, type, payload);
};
There are times when you wish to initiate the save draft process externally for example is we use elements.bottom_bar=false
to use custom bottomBar or use custom saveDraft button. For such cases, we provide the method triggerSaveDraft
const planner = await RoomleConfiguratorApi.createPlanner(
'demoConfigurator',
document.getElementById('planner-container'),
options,
);
planner.ui.callbacks.onSaveDraft = (id, image, url, { type, payload }) => {
console.log(id, image, url, type, payload);
};
document.getElementById('trigger-save').addEventListener('click', async () => {
await planner.ui.triggerSaveDraft('user@example.com');
console.log('save-draft-triggered');
});
onSaveDraft
parameters:
id
: A unique string identifier for the configuration Id or the plan Id.
image
: The image of the current configuration.
url
: The generated url from save draft.
data
: An object containing:
type
: It specifies the kind of draft. It can be either plan
if you trigger save from the planner or configuration
of you trigger save from the configurator.
payload
: The actual saved data, which could be the configuration object or plan snapshot data.
When you use the triggerSaveDraft
method, it saves the current configuration and then activates the onSaveDraft
callback after triggerSaveDraft
is finished. If you want to monitor and respond to actions from the custom save draft button, you can set up the onSaveDraft
callback and execute your custom code inside it. The returned data from this callback aligns with the saved data in Roomle's backend, facilitating functionalities like reloading the configuration based on the draft ID later on. This comes in handy when users wish to return to a previous configuration, be it after a short break, the following day, or even post browser restarts.
Using onSaveDraft, there is also discern patterns or statistics such as how many users who started a configuration opted to save their drafts. This metric offers insights into user behaviors, shedding light on potential improvements or user tendencies. Given the importance of this callback, we strongly advise incorporating it seamlessly into your
You can try and play around with this example her: (opens new window)
# Recipes
# Adding items to the scene
It’s possible to add additional products/items/configurations to the scene using the insertObject
API:
await interface.ui.insertObject('usm:frame');
By default the object is placed somewhere where it does not overlap with other items in the scene. You can also supply a position and a rotation for the object in the scene:
await interface.ui.insertObject('usm:frame', { x: -3, y: 0, z: 3 }, Math.PI);
Position (2nd parameter) is in meters and the rotation (3rd parameter) is in radians.
# Gets called when something is changed in the plan
It is possible to implement a behavior which gets called when there is something changed in the plan. It gets all objects of the plan as parameter.
interface.ui.callbacks.onPlanUpdate = (objects) => doSomething;
# React on specific plan element changes
You can react to certain plan element changes using the onPlanElementChanged(changeType)
callback.
Depending on what happened, the changeType
parameter can be:
added
: When a new plan element is added to the sceneremoved
: When an existing plan element gets removedchanged
: When a plan element is changed, like rotated or moved. But also when it is added or removedupdated
: When the configuration of a configurable product inside the scene is updated.
You can try and play around with this example here: (opens new window)
# React when tooltip is closed by controls button
You can react on any time when a tooltip is closed on controls button click As a parameter you get the name of the tooltip, e.g. "stopConfiguring"
interface.ui.callbacks.onTooltipClose = (tooltipName) => doSomething;
# Add products from external catalog
To integrate an external catalog into Rubens Room Designer and add products seamlessly to your design scenes, follow these steps:
Attach Events to Product Cards: Before you can make products interact from the external catalog to the Room Designer, ensure that you attached the proper events to it like
insertProduct
event, See the sample script below.Provide Event Data: you must adhere to the following API structure. This structure specifies the format for the message object that will be sent to the Room Designer, including the
eventType
andpayload
fields, which includes the productId.
export interface MessageEventData {
eventData: {
eventType: 'insertProduct';
payload: {
productId: string;
};
};
}
- Ensure CORS Compatibility: Make sure that the external catalog is embeddable. For more information see CORS Integration Guide
# Integration Example
You can use the following code as a reference for implementing interaction features with the Room Designer. This script adds necessary actions based on the data-product-id
attribute to handle product addition to the Room Designer scene. Customize this script to meet your specific requirements:
const EVENTS_MAP = {
CLICK: { eventName: "insertProduct", nativeEventName: "click" }
};
const sendEvent = (eventType, productId) => {
const parentWindow = window.parent;
if (parentWindow) {
const eventData = {
eventType,
payload: {
productId
}
};
parentWindow.postMessage({ eventData }, "*");
}
};
const addEventHandlers = ({ eventName, nativeEventName }) => {
const elementsWithEvent = document.querySelectorAll(
`[data-product-id]`
);
elementsWithEvent.forEach((element) => {
const productId = element.getAttribute("data-product-id");
const eventHandler = (event) => {
sendEvent(eventName, productId);
};
element.addEventListener(nativeEventName, eventHandler);
});
};
const removeEventHandlers = ({ nativeEventName }) => {
const elementsWithEvent = document.querySelectorAll(
`[data-product-id]`
);
elementsWithEvent.forEach((element) => {
if (element.eventHandler) {
element.removeEventListener(nativeEventName, element.eventHandler);
delete element.eventHandler;
}
});
};
const addClickHandlers = () => {
addEventHandlers(EVENTS_MAP.CLICK);
};
const removeEventListeners = () => {
removeEventHandlers(EVENTS_MAP.CLICK);
};
window.addEventListener("load", () => {
addClickHandlers();
});
window.addEventListener("beforeunload", removeEventListeners);
# CORS Integration Guide
CORS (Cross-Origin Resource Sharing) is a security feature implemented by web browsers. It controls which web pages are allowed to make requests to different domains. CORS prevents malicious websites from making unauthorized requests.
# Understanding CORS Errors
When a CORS issue occurs, the browser typically returns one of the following error messages:
CORS Request Blocked: The browser blocks the request because it violates the same-origin policy.
No 'Access-Control-Allow-Origin' Header: The server does not include the required CORS headers in its response.
Preflight Request Did Not Succeed: The browser sends a preflight OPTIONS request, and it does not receive a successful response from the server.
Request Blocked Due to Preflight Response: The server's response to the preflight OPTIONS request indicates that the actual request is not allowed.
# How to Identify a CORS Issue
You may encounter a CORS issue if:
- You see a CORS-related error message in the browser's developer console.
- Your AJAX or fetch requests to an external domain are being blocked.
- You notice that the content from the external domain is not loading or is displaying incorrectly on your web page.
# Dealing with CORS Issues
There are several solutions to address CORS issues:
Server-Side Proxy: Consider setting up a server-side proxy that fetches content from your server and serves it to our app. This way, our app makes requests to the same domain (the proxy server), avoiding CORS issues.
CORS Headers: Configure your server to include the necessary CORS headers in its responses. The key header is Access-Control-Allow-Origin, which specifies which domains are allowed to access your resources.
# Configuration Options for Different Scenarios
The approach you choose depends on your specific integration scenario:
If You Control the Server: Modify your server's CORS configuration to allow requests from our app's domain. Consult your server's documentation for guidance on CORS configuration.
If You Don't Control the Server: Consider using a server-side proxy.