Understanding Target State
The targetState
data structure defines where an app will render. It is responsible for assigning an app to specific fields, sidebars and editors in the web app during the configuration of an app as well as setting any instance parameters required by the app. We use the term targetState
because it refers to the state of an environment after the app is configured.
Understanding the targetState
object
To get a better understanding when and how targetState
is used, we will go through an example. The following code shows a component that is used for the app.
class Config extends Component {
// We are skipping the render example because it is not relevant to this example.
render() { ... }
// This method will be called when a user clicks on "Install"
// or "Save" in the configuration screen.
//
// We want to do two things in here:
// 1. Persist selected values as a parameter
// 2. Assign the app to sidebars of all Content Types
async onConfigure() {
// Get current state
// This will be used as base for next state updates
const currentState = await sdk.app.getCurrentState();
// The return value of `onConfigure` is used to install
// or update the configuration.
return {
// Parameters to be persisted as the app's installation parameters.
parameters: { ... },
// Transformation of an environment performed in the
// installation process.
targetState: {
EditorInterface: {
// Always pass in the `currentState` to avoid
// removing any previous location configuration
...currentState?.EditorInterface,
// A content type id where we will assign the app to the sidebar
'my-content-type-id': {
// assignment to sidebar in position 0 (will show up at the very top of the sidebar)
sidebar: { position: 0 }
}
}
},
};
}
}
The targetState
is part of the object that you can optionally return in your onConfigure
function. The keys of the EditorInterface
are the IDs of content types we want to configure for the app. In the code example above, my-content-type-id
is the ID of a content type that will have our example app in the sidebar at position 0, which means it will be the first thing you see. Apps can leverage different locations
to cover different use cases. It is especially important to note here that we always include the currentState
to preserve the current EditorInterface
state upon configuration updates.
In the example above we were only interested in installing the app in the sidebar location. Below, we will illustrate how to install an app into different locations.
targetState
key in the object returned by onConfigure
will remove any previous app location assignment and instance parameters!
Entry editor location
If you want an app to be rendered as a new entry editor, you need to specify the key editors
property for the desired content type. The expected value is an object whose position
represents the location (zero-based) of the editor in the list of editors.
The editors object can also contain an optional key settings
which can be used to set any instance parameters used by the app.
async onConfigure() {
// Get current state
// This will be used as base for next state updates.
const currentState = await sdk.app.getCurrentState();
// The return value of `onConfigure` is used to install
// or update the configuration.
return {
// Parameters to be persisted as the app configuration.
parameters: { ... },
// Transformation of an environment performed in the
// installation process.
targetState: {
EditorInterface: {
// Always pass in the `currentState` to avoid
// removing any previous location configuration.
...currentState?.EditorInterface,
'my-content-type-id': {
editors: {
// Places the entry at the second position
// (position = 1) in the editors list for the
// content type with ID `my-content-type-id`.
position: 1,
// Set values for this location for the app's instance parameters
settings: {
parameterOne: true,
anotherParamter: 'orange'
}
}
}
}
},
};
}
Field location
If you want an app to render as an entry field, specify the controls
property with the field IDs of the content type you want to replace with your app.
Each control object can also contain an optional key settings
which can be used to set any instance parameters used by the app.
async onConfigure() {
// Get current state
// This will be used as base for next state updates
const currentState = await sdk.app.getCurrentState();
// The return value of `onConfigure` is used to install
// or update the configuration.
return {
// Parameters to be persisted as the app configuration.
parameters: { ... },
// Transformation of an environment performed in the
// installation process.
targetState: {
EditorInterface: {
// Always pass in the `currentState` to avoid
// removing any previous location configuration
...currentState?.EditorInterface,
// Render the app as two field editors of the content
// type with ID `another-content-type` (field: `my-field-id`)
'my-content-type-id': {
controls: [
{ fieldId: 'my-field-id' },
{ fieldId: 'a-field-with-instance-parameters', settings: { parameter: true } },
]
}
}
},
};
Entry sidebar location
If you want an app to render in the sidebar of your entry editor, you need to specify the sidebar
property as the key for your desired content type. The position
value refers to the order in which the app is rendered in the sidebar. 0
means that the app is rendered at the first position.
The sidebar object can also contain an optional key settings
which can be used to set any instance parameters used by the app.
async onConfigure() {
// Get current state
// This will be used as base for next state updates
const currentState = await sdk.app.getCurrentState();
// The return value of `onConfigure` is used to install
// or update the configuration.
return {
// Parameters to be persisted as the app configuration.
parameters: { ... },
// Transformation of an environment performed in the
// installation process.
targetState: {
EditorInterface: {
// Always pass in the `currentState` to avoid
// removing any previous location configuration
...currentState?.EditorInterface,
'my-content-type-id': {
sidebar: {
// Render the app on the top (position = 0) of the
// sidebar for the content type with ID `my-content-type-id`.
position: 0,
// Optionally set instance parameters used by the app
settings: {
parameterOne: true,
anotherParamter: 'orange'
}
}
}
}
},
};
}
Entry dialog location
Dialog locations are enabled by default and do not require any extra properties in the targetState
.
Page location
Since the page location is its own route in the web app and does not interfere with the EditorInterface
at all, there is no extra property needed for the targetState
.
Multiple content types and locations
Since the targetState
is a plain JavaScript object, we can add multiple keys to the EditorInterface
property. This allows us to support multiple content types and location configurations to build more complex apps. The following example defines a targetState
using three content types.
async onConfigure() {
// Get current state
// This will be used as base for next state updates
const appState = await sdk.app.getCurrentState();
// The return value of `onConfigure` is used to install
// or update the configuration.
return {
// Parameters to be persisted as the app configuration.
parameters: { ... },
// Transformation of an environment performed in the
// installation process.
targetState: {
EditorInterface: {
// Always pass in the `currentState` to avoid
// removing any previous location configuration
...currentState?.EditorInterface,
// Places the entry at the second position in the editors
// list for the content type with ID `my-content-type-id`
'my-content-type-id': {
editors: { position: 1 }
},
// Places the entry at the first position in the editors
// list for the content type `another-content-type-id`
'another-content-type-id': {
editors: { position: 0 }
},
// Render the app as two field editors
// and render the app on the top (position = 0) of the
// sidebar for the content type with ID `another-content-type`
'another-content-type': {
sidebar: { position: 0 },
controls: [
{ fieldId: 'title' },
{ fieldId: 'metadata' }
]
}
}
},
};
}