# Embedding

The embedding is used when you want to integrate the configurator "as it is" into your website or webshop. If you have massive adjustment wishes also consider using our SDK (opens new window)

# The 10.000 feet view

At first, we want to have a quick look at the concepts and ideas behind the embedded Roomle Configurator.

On the one hand, there is your webshop and on the other hand, there is the Roomle Configurator. The configurator runs client-side which means there is no need for any server-side language like PHP etc. Everything just happens in plain old JavaScript.

To ease the communication between your website and the Roomle Configurator we provide a small JavaScript library that can be used to call methods of the configurator or to subscribe to events.

Those methods and events give you the ability to flexibly integrate the configurator as you need it.

This tutorial will guide you through some uses cases and should give you the knowledge to implement whatever you need.

# Getting started

As stated above the communication happens with the help of a JavaScript library. Therefore you need to integrate the library into your project. This can either happen via a package manager (e.g. npm, yarn...) or a simple HTML script tag.

All the needed files can be found here: https://www.npmjs.com/package/@roomle/embedding-lib (opens new window)

You can either download the tar.gz or if you use npm you could do the following:

npm install @roomle/embedding-lib --save

Always specify the correct configuratorId in the init options (more details on that later). The configuratorId is handed over to you by your Roomle Contact Person. The correct configuratorId prevents attackers from simply copying your code and use the configurator on their website. The only thing you have to do is, to specify the domains on which the Roomle Configurator should be available. For example: alpha.roomle.com and www.roomle.com. You can do this either in the Roomle Product Data Cloud or by telling your Roomle Contact Person. If something is wrongly configured you will see a message on the screen and further information in the JavaScript error console. If you can not figure out what is wrong please contact your Roomle Contact Person.

# Copy & Paste without package manager

This section shows a simple quick start. We always recommend using a package manager and a good built step but to quickly illustrate the idea of the Roomle Configurator these examples should be sufficient.

<html>
    <head>
        <style>
            body {
                margin: 0;
                padding: 0;
                overflow: hidden;
            }
            #configurator-container {
                width: 1200px;
                height: 675px;
            }
        </style>
    </head>

    <body>
        <div id="configurator-container"></div>
        <script src="./roomle-configurator-api.es.min.js" type="module"></script>
        <script type="module">
            import RoomleConfiguratorApi from './roomle-configurator-api.es.min.js';
            (async ()=> {
                const options = {
                    id: 'usm:frame',
                };
                const configurator = await RoomleConfiguratorApi.create(
                    'demoConfigurator',
                    document.getElementById('configurator-container'),
                    options,
                );
            })();
        </script>
    </body>
</html>

To try it out just copy&paste the HTML snippet from above into a file called index.html and serve it from a web server. For quick experiments, the npm package http-server (opens new window) is very nice.

# Some notes on the example

# Browsers

As you can see the Roomle Configurator uses ES6 modules. We highly recommend creating a fallback for older browsers. Since the Roomle Configurator is a 3D tool written in web technologies a good user experience is only possible with modern browsers. For users with old browsers (e.g. Internet Explorer) we recommend creating a different sales funnel and therefore a different user journey. Since the alternatives differ widely based on the website and webshop Roomle can not provide a default fallback.

# Iframe

Yes, the Roomle Configurator is integrated as an iframe. This is per se nothing bad. Iframes have a bad reputation because they are also used for displaying ads etc. But the Roomle Configurator is not an annoying banner it is an essential part of the UX for a potential customer. Big platforms like Youtube, Vimeo, Paypal are also integrating their widgets as iframe. Of course, there are technologies like Web Components but the iframe approach has several advantages in the use case of Roomle Configurator, e.g.: each WebAssembly instance is in its own process, it's easier to react on resize events, etc.

"Iframes are bad for SEO they told me". Yes, that's true but only if you put essential data into the iframes. The Roomle Configurator only has a 3D scene that has basically no information for search engines. Therefore it makes sense to optimize your landing page on the website and treat the iframe just like you would treat an image.

Since the embedding webshop has no control over the iframe it also can not track what happens inside the iframe. Therefore we send important events to the webshop so it is possible to track events. More about that in the Recipes section.

# TypeScript

We provide typings for the embedding lib. At the time of writing it is necessary to use "skipLibCheck": true because we rely on things like WebAssembly and WebGL. Those typings are not available in all projects and therefore skipLibCheck is needed. We work on providing better shims so that this reliance is not necessary.

# The code

In the example, we set the width to 1200px and the height to 675px. This is on purpose to explain one concept we encountered during our extensive research on UX and customer behavior. When you try to configure something it makes sense to use as much space as possible on the screen of the user. Otherwise configuring becomes a pain. At the time of writing Roomle has a limit for 1024px width screens (be aware that this is just a random value and could change at any point in the future). This means if the container in which the configurator is placed smaller than this width the configurator is called in a "view only" mode. When the configurator is in "view only" mode a button in the right bottom is displayed. If the user clicks on this button the configuration starts and the configurator opens in "full page" mode. If you do not want to rely on this button you can implement your own logic. Either you keep the button visible or you can also hide it. You only need to pass the following to the init options: {buttons: {startconfigure: false}}. In the following paragraphs we outline a custom behaviour of the "start" button.

When you tell the configurator to start it will open in a "full page" mode. You can try this out by adding the following code to the example from above (the full example can be found in the file 01_small_screen.html):

<style>
  #configurator-container {
      width: 800px;
      height: 600px;
  }
</style>
<button>Start configure</button>
<script>
  const button = document.querySelector('button');
  button.addEventListener('click', () => configurator.ui.startConfiguring());
</script>

When you did everything correct you should see a button at the beginning of the page. We add an event listener to the button which calls startConfiguring on a click event.

This should open the configurator in "full page" mode and all the controls for configuring should be visible.

So it's up to the embedding website how to embed the configurator. If you want to start in "view only" just add the configurator to a small container. If you want to start directly in "configure mode" add it to a bigger container.

On mobile devices most screens will be too small to achieve the required width therefore the configurator is always opened in "full page" mode (otherwise configuring would be a pain for the user).

To only show the "start now button" you can listen to the onResize event. How this works will be illustrated in the following example (the full example can be found in the file 02_resize_callback.html):

<style>
  #configurator-container {
      width: 800px;
      height: 600px;
  }
  button {
    display: none;
  }
</style>
<button>Start configure</button>
<script>
  const button = document.querySelector('button');
  configurator.ui.callbacks.onResize = (isDesktop) => {
    if (!isDesktop) {
      button.style.display = 'block';
    } else {
      button.style.display = 'none';
    }
  };
  button.addEventListener('click', () => configurator.ui.startConfiguring());
</script>

Now you should see the "start" button only if needed. Because we hardcoded the container width to 800px you will always see it. If you play around with this value you will see how the display changes.

If you just want to start the configurator and do not want to bother about the screen sizes you could change the example to the following (the full example can be found in the file 03_instant_start.html):

<style>
  #configurator-container {
      width: 800px;
      height: 600px;
  }
  button {
    display: block;
  }
</style>
<button>Start configure</button>
<script>
  const button = document.querySelector('button');
  const options = {
    id: 'usm:frame',
  };
  button.addEventListener('click', async () => {
    const configurator = await RoomleConfiguratorApi.create(
      'demoConfigurator',
      document.getElementById('configurator-container'),
      options,
    );
    configurator.ui.startConfiguring();
  });
</script>

Now, as soon as you click on the button the configurator is started and immediately and opened in the needed size.

All the currently available methods are available behind the ui object. So if you want to know what's possible you can either look on the TSDocs or just inspect the ui object.

You will recognize that there is also an entry for callbacks. This is basically the event system of the configurator. In the example, above we already outlined how to use the onResize event and the same applies to all the other events available on the ui.callbacks objects. All the details can be found in the TSDocs as well.

Now you should know the basics and the idea of how to use and integrate the Roomle Configurator. Now there is the time to play around and create great user experiences.

The next section will illustrate some "recipes"

# Upgrades

We follow the sematic versioning (opens new window) approach and to make sure no breaking change slips through we follow the Conventional Commits (opens new window) convention. Therefore always make sure to read through our changelog (opens new window) if you upgrade to a new major version of the embedding lib. All the other upgrades should be straight forward and you only need to increment the package version in your package manager.

# Migration guide

If you are comming from an old version of the Roomle Configurator the following migration guide gives you an overview what changed and why.

# Recipes

Here you will find some recipes for how you could implement certain use cases. This is not a complete list and there are no limits for your own creativity but these recipes should give you an idea of how some things could be implemented.

# Instantiation

We provide now two convenient ways to load a product into the configurator. Either you instantiate the Roomle Configurator without a product and use loadObject later or you pass in an ID as init-option. Both ways have their pros and cons. Let's review them quickly.

When you have a detail-page of a product in your webshop or website it makes sense to pass the ID as init-option (because you know which product your customer wants to see). This makes it possible that everything is loaded in parallel, the code of the configurator, and the content of your product. This speeds up the loading process. On the other hand, it makes no sense if you load some product when you do not know which product your customer wants to see in the configurator. This could be the case if the Roomle Configurator is opened on a list-view page where several products are listed. Here it makes sense to only instantiate the configurator. This only loads the code of the configurator. When the user then selects a product you can use the loadObject method to load this specific product.

const objectToLoadId = 'usm:frame:BB3BB3E7951BC15109B1FF86D78C95DE3FB46E9F78714C46FFA2DE91866A2C2B';
const configurator1 = await RoomleConfiguratorApi.create(configuratorId, domElement1, {id: objectToLoadId, ...overrideServerOptions});
// instantiate the second configurator, because you can now :-) and wait with the load of the object
// until the user clicks on a specific product
const configurator2 = await RoomleConfiguratorApi.create(configuratorId, domElement2, overrideServerOptions);
// load the object into the second configurator when the user clicks on a specific button
document.getElementById('button-to-load-product-x').addEventListener('click', () => {
  configurator2.ui.loadObject(objectToLoadId);
});

The full example can be found in the file 00_init.html).

# Calculate a price based on the current part list

There are two options for the prices, either you use the prices you have in your webshops database or all the prices are calculated with the Roomle Price Service. This recipe shows a pseudo-code implementation of how you could calculate prices based on your webshops database. Therefore the variable priceDataBase is just a fake implementation of your own database. You can also fetch prices asynchronously with the fetch-api (opens new window).

To get all the parts of a configuration you need to register to the onPartListUpdate event. Then with all the parts, you can do your price calculation. To show the price within the configurator you need to call the method setPrice. (the full example can be found in the file 04_price.html)

// fake implementation of a database
// only for showcase purpose
const priceDataBase = {};
configurator.ui.callbacks.onPartListUpdate = (partList) => {
    const parts = partList.fullList;
    let priceSum = parts.reduce((sum, part)=> {
        if (!priceDataBase[part.articleNr]) {
            priceDataBase[part.articleNr] = Math.random() * 10;
        }
        return sum += priceDataBase[part.articleNr];
    }, 0);
    const shippingCosts = 30;
    // Tell the Roomle Configurator to show the current price
    configurator.ui.setPrice('€', priceSum + shippingCosts);
};

# Notes about onPartListUpdate

The onPartListUpdate callback always fires when the user of your webshop changes the configuration. It is also called on the initial load so that your implementation always has the most recent information about the current state of the configuration. The callback is executed with two parameters, parts and hash. The parts parameter is an array of parts. Based on this array you can do whatever calculation you need to do. For example calculate a price, shipping costs or packaging information. Sometimes those calculations can be complex and therefore we provide a hash. This has is not an ID it is only a representation of the current state of the configuration. If you see the same hash again you could short cut possible expensive calculations. Use the hash only for "performance optimization" during runtime. The hash is not saved to our database. A possible use-case for the hash could be the following (pseudo-code):

const lastHash = null;
configurator.ui.callbacks.onPartListUpdate = (partList, hash) => {
    const parts = partList.fullList;
    if(lastHash === hash) {
        return;
    }
    lastHash = hash;
    const priceSum = veryExpensivePriceCalculation(parts);
    const shippingCosts = 30;
    // Tell the Roomle Configurator to show the current price
    configurator.ui.setPrice('€', priceSum + shippingCosts);
};

# Typical challenges with the part list

If your webshop and ERP system can already handle all article numbers which could arise during a configuration then everything is fine and things like price calculations are simple arithmetic operations. But especially webshop systems are not always designed to handle highly individually customized configurations of a product. Let's consider the following example with two products (one shelf-frame and a combinded shelf-frame):

Example USM Frame

You see two different shelfs. A very simple approach for the webshop would be to setup two products in the webshop backend. For a configurable product this is not sufficient because you would need to create a product in the webshop backend for every possible configuration. This is why the Roomle Configurator returns a part list with the smallest combinable and customizable pieces. In the case of this shelf the part list consists on all the construction elements. Let's see why we can not simply add two whole shelf-frames to the part list.

For example: one shelf-frame consists of 8 circle-connectors and 4 feets. If you would simple duplicate it if there is a second shelf-frame attached you would have 16 circle-connectors and 8 feets. But the construction logic for this specific product is different. Because some parts can be shared between the two shelf-frames if they are docked together, only 12 circle-connectors and 6 feets are needed.

The following graphic should give you a better idea:

Parts

How parts are combined and added is very special for every product and therefore it is important to identify which things are the smallest possible parts. If those questions are addressed during content setup and also during the concept phase on how to connect the Roomle Configurator to existing systems (webshop, ERP etc) it will be easy to do the actual implementation later.

Furthermore it is also sometimes challenging for certain webshop systems to add configured products to the shopping cart. To think how to adjust your webshop system to accept also configured products in the shopping cart is another important part.

# Use the Roomle Price Service

If you do not have a webshop or a price database it could make sense to use the Roomle Price Service. The first step to use the Roomle Price Service is to enable the service. Therefore please speak with your customer success manager. If everything is in place you only need to provide the correct init options. Here is an example:

const configurator = await RoomleConfiguratorApi.create(
  'demoConfigurator',
  document.getElementById('configurator-container'),
  {...options, usePriceService: true},
);

Please also have a look on the localization section on how to load different price lists based on the location of your webshop.

If you see the error message prices are empty on the Chrome Dev Tools console it is possible that your account setup depends on a deprecated parameter option. For more details read the migration guide

# React on analytics events

Currently we pass out certain events to the embedding webshop those events are based on Goolge Tag Manager. You can find all the information about gtag.js (opens new window) on the Google website. The gist how this could be used is here (the full example can be found in the file 05_analytics.html):

const configurator = await RoomleConfiguratorApi.create(
  'demoConfigurator',
  document.getElementById('configurator-container'),
  {...options},
);
const fakeGtag = () => undefined;
configurator.analytics.callbacks.onGATracking = function () {
  console.log('Analytics event from iframe');
  fakeGtag(...arguments);
};

# Skinning

If you want to adjust the colors of the configurator there are two options:

  • Primary color: this is the main color which indicates highlightings
  • "Call to action" color: this color indicates special areas in the UI, things like active state (e.g.: are measurements visible) or conversion "points" like save draft

The Roomle Configurator will determine which color to apply "on" primary and "call to action" color. For example: if a box has the primary color the "on primary color" will define the color of the text. The same applies for "call to action" color and the "on call to action color".

The Roomle Configurator will decide between "black" or "white" for the "on" colors. If this does not suit your needs you can define your own "on" colors. If you want to rely on the Roomle selection for the "on" colors please only specify valid CSS RGB hex values, e.g.: #fff000 or #f30. Things like rgba(0,0,0,0.7) won't work and you have to specify the "on" colors yourself (the full example can be found in the file 06_skinning.html):

const configurator = await RoomleConfiguratorApi.create(
  'demoConfigurator',
  document.getElementById('configurator-container'),
  {
    ...options,
    skin: {
      'primary-color': '#1d68bd', // optional but please use a CSS RGB hex like #ff00ff if you want to rely on the color detection see explaination above
      'color-on-primary': '#f4e440', // optional, Roomle can decide this for you
      'cta-color': '#980d3f',  // please use a CSS RGB hex like #ff00ff if you want to rely on the color detection see explaination above
      'color-on-cta': '#8e8e8e', // optional, Roomle can decide this for you
    },
  },
);

# React on button clicks

You can use the onButtonClicked callback to react on button clicks within the configurator UI. The parameter of the callback is the name of the button which has been clicked.

You can use the UI_BUTTON enum to get all available button names:

export enum UI_BUTTON {
  AR = 'ar',
  PARTLIST = 'partlist',
  MULTISELECT = 'multiselect',
  DIMENSIONS = 'dimensions',
  FULLSCREEN = 'fullscreen',
  RESETCAMERA = 'resetcamera',
  RENDERIMAGE = 'renderimage',
  ADDONS = 'addons',
  REQUESTPRODUCT = 'requestproduct',
  SAVEDRAFT = 'savedraft',
  STARTCONFIGURE = 'startconfigure',
  PAUSECONFIGURE = 'pauseconfigure',
}

Example:

configurator.ui.callbacks.onButtonClicked = (name) => {
    console.log('Button clicked: ' + name);
};

# Show/hide certain buttons

In some situations, there is a need to hide certain buttons. For example, you want to hide the part list button because the part list contains info you do not want to show to everybody. To do this you need to pass in the correct init options:

const configurator = await RoomleConfiguratorApi.create(
  'demoConfigurator',
  document.getElementById('configurator-container'),
  {...options, buttons: {partlist: false}},
);

The key of the buttons hash in the options are defined by the UI_BUTTON enum which was shown one section above.

# Listen to onRequestProduct

To be notified when the user clicks on the checkout-call-to-action button in our iframe you need to define the onRequestProduct callback.

const configurator = await RoomleConfiguratorApi.create(
  'demoConfigurator',
  document.getElementById('configurator-container'),
  options,
);
configurator.ui.callbacks.onRequestProduct = (configurationId, image, partlist, price, labels, configuration) => {
  console.log(configurationId, image, partlist, price, labels, configuration);
};

Of course there are situations where you want to trigger this action from outside. Therefore we provide the method triggerRequestProduct. When you call this method the same process starts as if the user would have clicked on the checkout-call-to-action button. This means that you only need to subscribe to the onRequestProduct callback to handle those situations.

const configurator = await RoomleConfiguratorApi.create(
  'demoConfigurator',
  document.getElementById('configurator-container'),
  options,
);
configurator.ui.callbacks.onRequestProduct = (configurationId, image, partlist, price, labels, configuration) => {
  console.log(configurationId, image, partlist, price, labels, configuration);
};
document.getElementById('trigger-request').addEventListener('click', async () => {
  await configurator.ui.triggerRequestProduct();
  console.log('trigger-request-done');
});

When the onRequestProduct callback fires it is assured that the configuration is saved to the Roomle backend. The data which you get from the callback are therefore the same as in the Roomle backend. This enables you to do ceratin things like: reloading the configuration later based on the configuration ID. This is especially helpful if the user adds a configuration to the shopping cart and continues later (maybe next day or after a restart of the browser etc.). Furthermore the onRequestProduct callback can be used to calculate a conversion rate (e.g.: how many users who configured actually add something to the cart). As you can see this callback is a very imporant one and we highly recommend not working around this callback.

# Add product variations

If you have different variations of your product and want to show them to your webshop users it makes sense to activate the variations feature. Variations could be products with different materials but we advise to use the variants feature for structural different product configurations, e.g.: the customer can start with a L-sofa but you have various different types of sofas or you start with a lowboard shelf but the same collection also has a highboard.

In other words, the variations feature should help the user of your webshop to explore the collection of a product. Of course the user could configer from on variation to the next variation but this is time consuming and reduces the chance that the user discovers all meaningful options.

To enable the variation feature some steps are necessary:

The first thing involves data-setup in the PDC (opens new window):

  • you need to create different variations of your base product
  • you need to assign the variations to a tag

After that you can create a "variations map", which is basically a JSON key/value pair. The key is the ID of the root component and the value is the ID of the tag, an example could look like:

const options = {
  variations: {
    'usm:frame': 'tag_id_usm',
    'vitra:chair': 'tag_id_vitra'
  }
};

Now everytime a configuration which is based on the root component usm:frame the variations from the tag tag_id_usm are loaded and displayed in the configurator. If you change to something which is based on vitra:chair the variations from tag_id_vitra are loaded. If you load something which is not based on one of them no variations are loaded.

This behaviour is needed that the configurator only shows relevant variations which have something in common with the initial loaded configuration.

const configurator = await RoomleConfiguratorApi.create(
  'demoConfigurator',
  document.getElementById('configurator-container'), {
    ...options,
    variations: {
      'usm:frame': 'tag_id_usm',
      'vitra:chair': 'tag_id_vitra'
    }
  },
);

The full example can be found in the file 08_variations.html)

# Embed without JS interaction

In case you only want to include the Roomle 3D scene without any interaction to your webshop you can simply do that by setting the src of an iframe. Therefore you only need to add the query param api=false. This means the JavaScript API is not loaded and therefore the Roomle 3D scene does not wait for instructions of your webshop (e.g.: which object to load). A simple example would consist out ouf the query params: id (what object to load) and api=false:

<iframe src="https://www.roomle.com/t/cp/?id=usm:frame&api=false" width="560" height="315"></iframe>

We want to mention that we suggest to use the JavaScript API because it gives you more control about many things. Especially about versioning and settings. Furthermore you only have very limited options for customizing the Roomle Configurator and also you loose a lot of interaction possibilities which could lead to better user engagement. If you want to do customizations like skinning etc. you have to use the JavaScript API. Nevertheless in some cases the embedding without JavaScript API makes perfect sense and has a valid use-case.

# Load different products into the configurator

Since the Roomle Configurator is a single-page-app it is very easy to load different products into the configurator. If you are unsure what a single-page-app (SPA) is then it makes sense to read through the various explainations on the internet for example like this discussion (opens new window). If the concept of a SPA makes sense to you, let's have a look on the code:

<div class="product" data-roomle-id="__SOME_ROOMLE_ID_1__"><div>Product 1</div></div>
<div class="product" data-roomle-id="__SOME_ROOMLE_ID_2__"><div>Product 2</div></div>
<div class="product" data-roomle-id="__SOME_ROOMLE_ID_3__"><div>Product 3</div></div>
const buttons = document.querySelectorAll('.product');
[...buttons].forEach((button) => {
  button.addEventListener('click', (productDomNode) => {
    const target = productDomNode.target;
    let roomleId = target.getAttribute('data-roomle-id') || target.parentElement.getAttribute('data-roomle-id');
    if(!roomleId) {
      return;
    }
    configurator.ui.loadObject(roomleId);
  });
});

The full example can be found in the file 09_different_products.html.

# Localization

There are two different parameters to set the localization of the Roomle Configurator. First there is locale and then there is overrideCountry. If they are not provided both of them are inferred by the Roomle Configurator based on the browser settings and the location of the user of your webshop. Most of the time this is not 100% accurate. Let's have a look why on the example of locale. To set the language for the configurator we use locale. If a user visits your German webshop but has the browser settings switched to Spanish the configurator would be in Spanish but your webshop is in German. Since the Roomle Configurator does not know that it is embedded on the German webshop it's the best guess we can make. But if the webshop sets the locale we can use the same language as in the webshop. Therefore we highly recommend using this parameter. The same challenge arises for overrideCountry. The country parameter is used for the Roomle Price service. Based on the country we decide which price list to load. For example the price list with pounds for the UK webshop and the euro prices for the webshop based in EU. For both parameters we use two digits codes for locale we use the ISO_639-1 and for country ISO_3166-1_alpha-2, you can find the list for languages for example on ISO_639-1 (opens new window) the same is true for the country codes ISO_3166-1_alpha-2 (opens new window).

Let's have a look on the following code:

const configurator = await RoomleConfiguratorApi.create(
  'demoConfigurator',
  document.getElementById('configurator-container'),
  {...options, locale: 'de', overrideCountry: 'at', usePriceService: true },
);

With this snippe the webshop tells the Roomle Configurator that it should use the language de and the country at. Which basically means: "show the configurator in German and with the prices for the Austrian market". As explained above the overrideCountry is only needed if your webshop does not maintain the prices and you rely on the Roomle Price Service.

# Opening an existing configuration again

As a webshop you often want to load a specific configuration again. There could be different use-cases e.g.: sharing a configuration with other people or giving the user the chance to continue a configuration. We recommand to create a landing page which can handle Roomle Configuration IDs (be aware of the difference between a hash and a Roomle Configuration ID). Probably one of the easiest ways is, to pass the ID as query param. Let's see some code:

const urlParams = new URLSearchParams(window.location.search);
const roomleId = urlParams.get('roomleId');
if(!roomleId) {
  console.warn('Please add the query param roomleId to the URL, as test value you can use: usm:frame:68AB2631D0E02A9FBBCA1371621CD23BB68818685738868D7DEB5B830BD5CD2D');
  return;
}
configurator.ui.loadObject(roomleId);

You can find the full code in the following file 10_landing_page.html.

# Using different light settings

We try to provide a default light setting which works with the majority of products. Nevertheless there are certain products which require a special tweaking of those light settings. We provide a set of pre-defined light settings. Currently we support:

  • sofa: we created this light setting especially for sofas
  • shelf: the shelf light setting is optimized for shelfs
  • shelf_front: the shelf_front is optimized for shelfs with deep drawers so that there is not too much shadow inside them
  • equal: is a balanced light
  • camera: light comes always from the camera perspective
  • baked: light is set accordingly to the baked shadows

To use a special light setting you only need to provide the ls parameter in the options. In code this looks like:

const configurator = await RoomleConfiguratorApi.create(
  'demoConfigurator',
  document.getElementById('configurator-container'),
  {...options, ls: 'sofa' },
);

To switch through the different light settings you could try the following example: 11_light_settings.html.

# Send saved configuration to the e-mail of the user

To send a saved configuration to the e-mail address of the user you need to the set parameter emails to true. By default we only show the link to the configuration. Without any configuration the e-mail will contain a link back to Roomle. If you want to link to your webshop please make sure that you have a page which can open existing configuration. How this works is explained in the section Opening an existing configuration again. If you setup this page and everything is in place then you can set the deeplink parameter. The deeplink parameter should be a URL to a page in your webshop and containing the following placeholder: #CONFIGURATIONID#. This placeholder is then replaced by the real Configuration ID. The following code snippet shows the setup for the configurator.

Important: please also make sure that the deeplink is also set in the database of Roomle and linked to the configuratoId you are using. If this is not done, the link in the e-mail won't be replaced. If you encounter any problems, please contact your Roomle Contact Person.

const configurator = await RoomleConfiguratorApi.create(
  'demoConfigurator',
  document.getElementById('configurator-container'),
  {...options,
  emails: true, deeplink: 'https://www.roomle.com/t/cp/?id=#CONFIGURATIONID#&configuratorId=demoConfigurator' },
);

The full example can be found here: 12_email.html.

# Grouping the part list by main component

The part list is an object which has the following TypeScript interface:

interface PartList {
  originPart: Part;
  perMainComponent: PartList[];
  fullList: Part[];
}

For things like calculating a price etc most of the time the fullList is needed but for UI purposes it often make sense to group parts together so that the user understands better what she/he configured. To display the part list grouped just add the following option to the init parameters: groupPartList: true, for example like:

const configurator = await RoomleConfiguratorApi.create(
  'demoConfigurator',
  document.getElementById('configurator-container'),
  {
    ...options,
    groupPartList: true
  },
);

All the callbacks like onPartListUpdate will work the same as before. If you need access to the grouped part list just use the partList.perMainComponent inside the callback.