Skip to main content

How To Save And Load

In DynaMaker it is possible to save and load your configurations. For that you need to know about:

Requirements

Save and load requires that your application uses some form of authentication when deployed. This might be done via a plugin such as Auth0 or through an integration with your existing login solution with JSON Web Tokens.

JSON

To save a design, we convert it to a JSON object, which can be sent to the server and stored in a database. For this to work, each component must have a function called generateJSON that creates a JSON representation of its current configuration. Luckily, by extending the STUDIO.BaseComponent class, we get this for free.

To load a design, we need to parse the JSON that was saved to the database. To accomplish this, each component must have a static fromJSON function. Just like with generateJSON, this is added by extending the STUDIO.BaseComponent class.

Handling Breaking Changes

Let say we have a chair component that comes in red or black.

interface Properties {
color: 'red' | 'black'
}

class Component extends STUDIO.BaseComponent<Properties> {
protected properties: Properties

constructor() {
this.setProperties({ color: 'red' })
}
}

We deploy this application and our customers use it and save their configurations successfully. After a few months, our marketing department has decided that we should no longer produce any black chairs. They have also decided that any chairs that were saved as black should now be orange instead if loaded.

To deal with this mess, we implement custom generateJSON and fromJSON functions.

interface Properties {
color: 'red' | 'orange'
}

class Component extends STUDIO.BaseComponent<Properties> {
protected properties: Properties

constructor() {
this.setProperties({ color: 'red' })
}

generateJSON(componentIdMapping: ComponentIdMapping[]): ComponentJSON<Properties> {
return {
v: 2,
ch: this.componentHandler.generateJSON(componentIdMapping),
p: this.properties,
}
}

static fromJSON(json: ComponentJSON<Properties>, componentIdMapping: ComponentIdMapping[]) {
const component = new this()

if (json.v === undefined || json.v < 2) {
/**
* Chairs before version 2 which were black should now be orange instead.
* See https://docs.mycompany.com/design-specifications/123
*/
if (json.p.color === 'black') {
json.p.color = 'orange'
}
}

component.properties = json.p

if (json.ch) {
component.componentHandler = ComponentHandler.fromJSON(json.ch, componentIdMapping)
}

return component
}
}

By applying this pattern we can gather all the breaking changes in one place, which improves readability. By catching the change as early as possible when loading the saved design, we can also keep the rest of the code clean from checking values that were valid before but that should no longer be used.

Next time that we have to change something like this in the chair component, we simply increase the version v to 3 and add another if-statement.

generateJSON (componentIdMapping: ComponentIdMapping[]): ComponentJSON<Properties> {
return {
v: 3,
ch: this.componentHandler.generateJSON(componentIdMapping),
p: this.properties,
}
}

static fromJSON (json: ComponentJSON<Properties>, componentIdMapping: ComponentIdMapping[]) {
const component = new this()

if (json.v === undefined || json.v < 3) {
// Handle versions lower than 3 here
}

if (json.v === undefined || json.v < 2) {
/**
* Chairs before version 2 which were black should now be orange instead.
* See https://docs.mycompany.com/design-specifications/123
*/
if (json.p.color === 'black') {
json.p.color = 'orange'
}
}

component.properties = json.p

if (json.ch) {
component.componentHandler = ComponentHandler.fromJSON(json.ch, componentIdMapping)
}

return component
}

Save & Load Configurations

In order to save and load your configurations, you could add some menu items. For that:

  • Go to your UI Editor.
  • In UI within onInit(), create new buttons for loading and saving.
  • Use Studio.onConfigurationLoaded() to make sure you trigger the appropriate functions:
export function onInit() {
Studio.setMainMenuItems([
new SKYUI.Button('Save', () => {
LoadSave.openPopupSave()
}),
new SKYUI.Button('Load', () => {
LoadSave.openPopupLoad()
}),
])

Studio.onConfigurationLoaded(() => {
Studio.requestGeometryUpdate()
Studio.requestLightweightGeometryUpdate()
Studio.reloadActiveTab()
})

Studio.setTabs([
// app tabs
])

// other functions
}

Once you have been authenticated as user of the application (as required), you can try saving & loading your configurations. Each configuration can be referenced by a unique configurationId and a revision number. If the revision is omitted, the most recently created revision will be used.

Example URLs

URL of the app without loading any saved configuration:

https://deployed.dynamaker.com/applications/YOUR_APP_ID/

Configuration A1 (latest revision):

https://deployed.dynamaker.com/applications/YOUR_APP_ID/?configurationId=61e6c8cf98f9add1ec0a3f18

Configuration B2 (latest revision):

https://deployed.dynamaker.com/applications/YOUR_APP_ID/?configurationId=fa3f1871e6c89add1ec0cf98

Configuration B1 (first version):

https://deployed.dynamaker.com/applications/YOUR_APP_ID/?configurationId=fa3f1871e6c89add1ec0cf98&revision=1

Configuration B2 (second version, same as latest):

https://deployed.dynamaker.com/applications/YOUR_APP_ID/?configurationId=fa3f1871e6c89add1ec0cf98&revision=2

Remember to use Studio.getConfigurationInfo() to read the information of the configuration. You can read more about it here.