Back to Website
Show / Hide Table of Contents

Restoring the editor

  • 8 minutes to read

After the user has created a design and added a personalized product to the shopping cart, they may want to return to editing this design to fix any problems or use it for a new order. In other words, you need to be able to open the same config file but initialize it differently to restore the state of the editor at the moment when the user left off.

In simple cases, it may be a straightforward task. In most cases, all you need to do is to open a user design instead of an original template. However, if the config includes not only the editor but also some options, it may get complicated. You will need to restore user selections that may cause recalculation of the dynamic expressions in the config. Predicting side effects of that may be challenging.

To be able to restore the editor and maintain both the product options and design changes, Customer's Canvas offers the concept of snapshots and a special restoration section of the config. This article explains how to use them.

Snapshots

A snapshot is basically a state of all widgets in the editor represented by a base64-encoded string. UI Framework allows for applying this string later to restore the state of widgets. When it happens, it temporarily turns off the dynamic expression auto update to prevent any side effects and inconsistencies.

UI Framework allows for reading this string at any time. As usual, you want to do it when a user finishes personalization and leaves the editor. It is supposed that you store this string somewhere, for example, in local storage or a database that temporarily stores unfinished shopping carts. When a user decides to return to the editor, you need to pass a snapshot to the editor page and initialize the editor using this string.

All standard e-commerce plugins implement this idea. As long as a config file sets up a snapshot and a restoration section (see further), the "return to edit" functionality works out of the box. However, you can implement it in any custom integration as well.

Creating a snapshot

The simplest way to read a snapshot and pass it outside of the editor is to define the snapshot property in the data object of the order or cart widget as follows:

{
    "name": "order",
    "type": "order",
    "params": {
        "images": "{{ $['design-editor'].proofImageUrls.map(item=>item[0]) }}",
        "downloadUrls": "{{$['design-editor'].hiResUrls}}",
        "data": {
            "stateId": "{{ $['design-editor'].stateId}}",
            "snapshot": "{{ main.editorState }}"
        }
    }
}
Tip

All our standard e-commerce plugins read the snapshot from this snapshot property. In custom integrations, it is necessary to implement the ability to store this value, as explained below.

As you may notice, we're just reading the editorState property of the editor component (au-wizard HTML element), which is accessed through the main object of a scope.

You can also create a snapshot outside of the config in your JavaScript code. To make a snapshot, you can call the auWizard.createEditorSnapshot() method in your script.

const snapshot = await auWizard.createEditorSnapshot(true);

If you pass true as the parameter, then this method will also have all Design Editors used in the config to save the design by using the saveProduct method of the Customer's Canvas IFrame API.

Storing a snapshot

It is up to you where and how you store a snapshot. For example, you may use:

  • Local storage
  • Database
  • For the online store platforms like Shopify, cart item meta fields
  • ... and any other way to store temporary data

Note that a user may order more than one line item, or the same item may be ordered several times with different personalization. It may be important to take this into consideration, for example, if you are using local storage and select a key name for a snapshot entry.

It is not recommended to store this data too long. If you change the config after the snapshot is created, UI Framework may fail to restore the updated config with a snapshot.

Restoring the editor state

As usual, to allow the user to restore the editor, you are adding a button, for example, "Return to edit", on each line item in a shopping cart. When a user clicks it, you are opening the product page again and passing the snapshot through a query string or another method.

As usual, you add some code that detects whether a new personalization instance began or the previous one needs to be restored. In the latter case, you need to initialize the e-commerce driver in the following way:

let ecommerce = await driver.init(product, editor, config, 
                        { editorUrl: "https://{your-site-url}/design-editor/" }, 
                        /* restore data */ { snapshot: "N4I...." }, 
                        /* quantity */ 1, 
                        /* user info*/ { id: "JohnWood" });

Another way to do so is to call auWizard.restoreEditor():

await auWizard.restoreEditor(snapshot);

Using the restoration section

For most config files, the restoration process described above is sufficient. However, when the config logic is complex or relies on data obtained from the backend through the ajax widget, you may need to have finer control over this process.

This is where the restoration section of a config comes into play. It looks like this:

{
	"restoration": {
		"widgets": [...],
		"steps": [...],
		"vars": {...}
	}
}

When this section is presented, UI Framework will use values of widgets, steps, and vars specified inside restoration instead of the original ones. You may use different amounts of widgets or steps. If any of these values are omitted, they are taken from the original config.

Inside this section, you may use dynamic expressions with one additional object in a scope - widgetData. It allows for referring the widget data stored in a snapshot instead of the widget itself.

Example - using widgetData

For a better understanding of how it works, let's consider an example.

Imagine that you are building a personalization process where you want a user to select a vehicle using three dropdown elements (option widget) - vehicle year, make, and model. Depending on these values, a user selects a design with an appropriate mockup and creates artwork for a particular vehicle.

So, your original config will consist of two steps: 1) choosing a vehicle and 2) personalizing a design that corresponds to a selected vehicle.

These dropdowns are populated from the server, and a list of available values depends on other dropdowns. For example, if you choose "Ford" as a make and "2021" as a year, you would like to see only Ford models that are available in the 2021 model line.

This behavior works great when the user just started the personalization process and did not choose any values yet. However, when you return to the editor from the cart, a vehicle is already selected, and the only thing you need to do is restore the design editor tool. Reusing the same config and avoiding reloading the design without losing progress is quite a challenging task.

The restoration section comes to the rescue. It allows for simplifying a config loaded after the restoration and excluding widgets that may prevent the editor from restoring its state correctly.

The original config may look like this:

{
  "widgets": [
    {
      "name": "vehicleYear",
      "type": "ajax",
      "params": { /* omitted for brevity */ }
    },
    {
      "name": "vehicleMake",
      "type": "ajax",
      "params": { /* omitted for brevity */ }
    },
    {
      "name": "vehicleModel",
      "type": "ajax",
      "params": { /* omitted for brevity */ }
    },
    {
      "name": "order",
      "type": "order",
      "params": {
      "data": {
        "Year": "{{ $['vehicleYear']._.title }}",
        "Make": "{{ $['vehicleMake']._.title }}",
        "Model": "{{ $['vehicleModel']._.title }}"
      },
      "images": "{{ $['editor'].proofImageUrls.map(x=> x[0]) }}",
      "downloadUrls": "{{ $['editor'].hiResUrls }}"
      },
      // ...
    }
  ],
  // ... omitted for brevity
}

In the restoration.widgets, we need to get rid of the ajax widgets and refer the widgetData instead of these widgets:

{
  "widgets": [ /* the same */ ],
  "restoration": {
    "widgets": [
      {
        "name": "order",
        "type": "order",
        "params": {
          "data": {
            "Year": "{{ widgetsData.vehicleYear.selected.optionValue.title }}",
            "Make": "{{ widgetsData.vehicleMake.selected.optionValue.title }}",
            "Model": "{{ widgetsData.vehicleModel.selected.optionValue.title }}"
          },
          "images": "{{ $['editor'].proofImageUrls.map(x=> x[0]) }}",
          "downloadUrls": "{{ $['editor'].hiResUrls }}"
        }
      },
      // ...
    ],
    // ...
  },
  // ...
}

Accessing properties in widgetData

To access widget values in widgetData, you may need to use slightly different syntax. In the following table, you can learn how the basic properties of widgets match their representations in widgetsData.

Widget Property in config Property in widgetData
ajax response response
ajax responses responses
ajax statusCode status
asset-storage-ajax apiResponse apiResponse
asset-storage-ajax response response
canvas stateId stateId
checkbox ._ value
color-picker color color
color-selector color color
data-driven-editor stateId design.designId
data-driven-editor userId access.userId
datasheet tableService.getData() tableData
datasheet columnSchemes columnSchemes
design-editor stateId stateId
gallery ._ value
html $.container.innerHTML (полный HTML) html
input-text ._ text
option ._ selected.optionValue
preflight stateId stateId
preflight page page
static-text text text

Design Editor in the restoration section

The Design Editor widget is processed in a special way. As you know from the Working with Design Editor article, its config has an initial section, which describes how the editor is loaded. It includes two parts: productDefinition and editorConfig.

When we are restoring the editor, we first execute the productDefinition, then load the design from the snapshot. So, it does not matter what productDefinition you specify - it can be any valid value, for example, a blank 1x1 design. It will be replaced by the saved design anyway.

As for the editorConfig, you might just copy the config from the original widget. However, it is almost always very large. To avoid copying a bulky config, you may just replace it with an object { "loadConfigFromState": true }. In this case, it will take this config from the saved state file.

Here is an example:

{
  "name": "editor",
  "type": "design-editor",
  "params": {
    "initial": {
      "productDefinition": {
        "surfaces": [
          {
            "width": 1,
            "height": 1
          }
        ]
      },
      "editorConfig": {
        "loadConfigFromState": true
       }
     }
  }
}
Was this page helpful?
Thanks for your feedback!
Back to top Copyright © 2001–2024 Aurigma, Inc. All rights reserved.
Loading...
    Thank for your vote
    Your opinion is important to us. To provide details, send feedback.
    Send feedback