Inspector
Inspector is a visual configuration tool that is used in several places of Winter backend. The most known usage of Inspector is the CMS components configuration feature, but Inspector is not limited to the CMS. In fact, it's a universal tool that can be used with any element on a backend page.
The Inspector loads the configuration schema from an inspectable element, builds the user interface, and writes values entered by users back to the inspectable element. The first version of Inspector was supporting only a few scalar value types - strings and Booleans, without an option to edit any complex data.
The current version of Inspector allows to edit any imaginable data structures, including cases where users create enumerable data elements right in the Inspector interface.
This section describes the client-side Inspector API without going into details about the backend usage of the data Inspector generates. Inspector accepts the configuration schema in JSON format and generates values in JSON format as well. Providing the configuration and interpreting the generated values is up to developers. For example, the CMS module uses information returned from component's defineProperties() method to generate the configuration JSON string and converts JSON values generated by Inspector to the components configuration in CMS templates. In this document we are focusing only on the JSON format.
Configuring inspectable elements
Clicking an inspectable element displays Inspector for that element. Any HTML element could be made inspectable by adding data attributes to it. The required attributes are:
-
data-inspectable
- indicates that Inspector should be created when the element is clicked. -
data-inspector-title
- sets the Inspector popup title. -
data-inspector-config
- contains the Inspector configuration JSON string. If this attribute is not specified, the configuration is loaded from the server, see the Dynamic configuration and dynamic items section below.
Inspectable elements should also contain a hidden input element used by Inspector for reading and writing values. The input element should be marked with the data-inspector-values
data attribute.
Example inspectable element markup:
<div
data-inspectable
data-inspector-title="Some inspectable element"
data-inspector-description="Some description">
<input
data-inspector-values
type="hidden"
value="JSON"/>
</div>
Optional data attributes
There are several optional data attributes and features that could be defined in an inspectable element or in elements around it:
-
data-inspector-offset
- sets offset, in pixels, for the Inspector popup. -
data-inspector-offset-x
- sets horizontal offset, in pixels, for the Inspector popup. -
data-inspector-offset-y
- sets vertical offset, in pixels, for the Inspector popup. -
data-inspector-placement
- sets defines placement for the Inspector popup, optional. If omitted, Inspector evaluates a placement automatically. Supported values: top, bottom, left, top. -
data-inspector-fallback-placement
- sets less preferable placement for the Inspector popup, optional. This value is used if Inspector can't use the placement specified in data-inspector-placement. Supported values: top, bottom, left, top. -
data-inspector-external-parameters
- if this attribute exists in any parent element of the inspectable element, the external parameters editors will be enabled in Inspector (unless property-specific rules cancel the external editor).
Dynamic configuration and dynamic items
In case if the data-inspector-config
attribute is missing in the inspectable element Inspector tries to load its configuration from the server. An important note - there should be a FORM element wrapping inspectable elements in order to use any dynamic features of Inspector.
The AJAX request used for loading the configuration from the server is named onGetInspectorConfiguration
. The handler should be defined in the backend controller and should return an array containing the Inspector configuration (in the PHP equivalent of the JSON configuration structure described later in this section), inspector title and description. Example of a server-side AJAX dynamic configuration request handler:
public function onGetInspectorConfiguration()
{
// Load and use some values from the posted form
//
$someValue = Request::input('someValue');
... do some processing ...
return [
'configuration' => [
'properties' => [list of properties],
'title' => 'Inspector title',
'description' => 'Inspector description'
]
];
}
Some Inspector editors - (drop-down, set, autocomplete) support static and dynamic options. Dynamic options are requested from the server, rather than being defined in the configuration JSON string. For using this feature, the inspectable element must have the data-inspector-class
attribute defined. The attribute value should contain a name of a PHP class corresponding to the inspectable element.
The server-side controller should use the Backend\Traits\InspectableContainer
trait in order to provide the dynamic options loading. The inspectable PHP class (specified with data-inspector-class
) must either have a method get[Property]Options()
, where the [Property] part corresponds the name of the dynamic property, or getPropertyOptions($propertyName)
method that is more universal and accepts the property name as a parameter. The methods should return the options
array containing associative arrays with keys option
and value
. Example:
public function getContextOptions()
{
$optionsArray = [];
$optionsArray[] = ['value' => 'create', 'title' => 'Create'];
$optionsArray[] = ['value' => 'update', 'title' => 'Update'];
$optionsArray[] = ['value' => 'delete', 'title' => 'Delete'];
return [
'options' => $optionsArray
];
}
Container and popups
By default Inspector is displayed in a popup, but there's an option to display it right on the page, in a container element. To enable this option, all inspectable elements should be wrapped into another element with data-inspector-container
attribute. The attribute value should be a CSS selector pointing to an element inside the wrapper. Example:
<div data-inspector-container=".inspector-container">
<div class="inspector-container"></div>
<div data-inspectable ... ...>
<div data-inspectable ... ...>
</div>
The inner element will act as host element for Inspector when an inspectable element is clicked. The element should have the inspector-container
class and can be optionally marked with data-inspector-scrollable
attribute to make the Inspector scrollable. For the scrolling feature, the container element should have height defined explicitly.
When the container is used, Inspector is still displayed in a popup by default, but users can click an icon in the Inspector header to move it to the container.
Data schema configuration
Inspector configuration, defined with data-inspector-config
attribute or loaded from the server, should be an array containing a list of property definition. All examples in this section use JSON format. Below is an example of a configuration for two properties:
[
{
"property": "firstName",
"title": "First name",
"type": "string"
},
{
"property": "lastName",
"title": "Last name",
"type": "string"
}
]
This configuration creates two text fields with titles "First name" and "Last name". When the data is saved back to the inspectable element (to the data-inspector-values
hidden input element), it would have the following format:
{"firstName":"John", "lastName":"Smith"}
Each property should have attributes property
, title
and type
. The type
attribute defines a type of an editor that should be created for the property. The supported editors are described further.
Other attributes supported by all (or most of the) property types are:
-
description
- description string, which is available in a tooltip displayed when a user overs the 'i' icon in the property editor. -
group
- allows to group multiple properties. The attribute should contain a group name. Groups could be collapsed by users, making the Inspector interface less cluttered. -
showExternalParam
- enables the inspector parameter editor for the property. External parameters are currently used only by the CMS. Note that some property types do not support external property editors. See alsodata-inspector-external-parameters
attribute described above. -
placeholder
- text to display in the editor if property value is empty. -
validation
- validation configuration. See the complete validation description below. -
default
- default property value. The property value format depends on the property type - for thestring
type it's an array, forstringList
type it's an array of strings. See more details below.
All other configuration properties are specific for different property types.
String editor
String editor allows entering a single line of a text and represented with a simple input text field. The editor doesn't have any specific parameters. The optional default
parameter for the editor should contain a string.
{
"property": "firstName",
"title": "First name",
"type": "string",
"default": "John"
}
The editor generates string values:
{"firstName":"Sam"}
Text editor
Text editor allows entering multi-line long text values in a popup window. The editor doesn't have any specific parameters. The optional default
parameter for the editor should contain a string.
{
"property": "description",
"title": "Description",
"type": "text",
"default": "This is a default description"
}
The editor generates string values:
{"description":"This is a description"}
String list editor
Allows users to enter lists of strings. The editor opens in a popup window and displays a text area. Each line of text represents an element in the result array. The optional default
parameter should contain an array of strings. Example:
{
"property": "items",
"title": "Items"
"type": "stringList",
"default": ["String 1", "String 2"]
}
A value generated by the editor is an array of strings, for example:
{"items":["String 1","String 2","String 3"]}
Autocomplete editor
This editor works like the string
editor, but includes the autocomplete feature. Autocompletion options can be specified statically, with the items
parameter or loaded dynamically. Example with static options:
{
"property": "condition",
"title": "Condition"
"type": "autocomplete",
"items": {"start": "Start", "end": "End"}
}
The items are specified as a key-value object. The items
parameter is optional, if it's not provided, the items will be loaded from the server - see Dynamic configuration and dynamic items section above.
Values generated by the editor are strings. Example:
{"condition":"start"}
Fields of this type do not support external property editors.
Checkbox editor
Properties of this type are represented with a checkbox in the Inspector UI. This property doesn't have any special parameters. The default
parameter, if specified, should contain a Boolean value or string values "true", "false", "1", "0". Example:
{
"property": "enabled",
"title": "Enabled",
"type": "checkbox",
"default": true
}
Values generated by the editor are 0 (unchecked) or 1 (checked). Example:
{"enabled":1}
Dropdown editor
Displays a drop-down list. Options for the drop-down list can be specified statically with the options
attribute or loaded from the server dynamically. Example:
{
"property": "action",
"title": "Action",
"type": "dropdown",
"options": {
"show": "Show",
"hide": "Hide",
"enable": "Enable",
"disable": "Disable",
"empty": "Empty"
}
}
The options
attribute should be a key-value object. If the attribute is not specified, Inspector will try to load options from the server - see Dynamic configuration and dynamic items section above.
The editor generates a string value corresponding to the selected option, for example:
{"action":"hide"}
Dictionary editor
Dictionary editor allows to create key-value pairs with a simple user interface consisting of a table with two columns. The default
parameter, if specified, should contain a key-value object. Example:
{
"property": "options",
"title": "Options",
"type": "dictionary",
"default": {"option1": "Option 1"}
}
The editor generates an object value, for example:
{"options":{"option1":"Option 1","option2":"Option 2"}}
The dictionary editor supports validation for the entire set (required
and length
validators) and for keys and values separately. See the validation description further in this document. The validationKey
and validationValue
define validation for keys and values, for example:
{
"property": "options",
"title": "Options",
"type": "dictionary",
"validation": {
"required": {
"message": "Please create options"
},
"length": {
"min": {
"value": 2,
"message": "Create at least two options."
}
}
},
"validationKey": {
"regex": {
"pattern": "^[a-z]+$",
"message": "Keys can contain only lowercase Latin letters"
}
},
"validationValue": {
"regex": {
"pattern": "^[a-zA-Z0-9]+$",
"message": "Values can contain only Latin letters and digits"
}
}
}
Object editor
Allows to define an object with specific properties editable by users. Object properties are specified with the properties
attribute. The value of the attribute is an array, which has exactly the same structure as the Inspector properties array.
{
"property": "address",
"title": "Address",
"type": "object",
"properties": [
{
"property": "streetAddress",
"title": "Street address",
"type": "string"
},
{
"property": "city",
"title": "City",
"type": "string"
},
{
"property": "country",
"title": "Country",
"type": "dropdown",
"options": {"us": "US", "ca": "Canada"}
}
]
}
The example above creates an object with three properties. Two of them are displayed as text fields, and the third as a drop-down.
Object editor values are objects. Example:
{
"address": {
"streetAddress":"321-210 Second ave",
"city":"Springfield",
"country":"us"
}
}
The object properties can be of any type supported by Inspector, including other objects.
There's a way to exclude an object from Inspector values completely, if one of the object fields is empty. The field is identified with ignoreIfPropertyEmpty
parameter. For example:
{
"property": "address",
"title": "Address",
"type": "object",
"ignoreIfPropertyEmpty": "title",
"properties": [
{
"property": "streetAddress",
"title": "Street address",
"type": "string"
},
{
"property": "city",
"title": "City",
"type": "string"
}
]
}
In the example above, if the street address is not specified, the object ("address") will be completely removed from the Inspector output. If there are any validation rules defined on other object properties and the required property is empty, those rules will be ignored.
A default
value for the editor, if specified, should be an object with the same properties as defined in the properties
configuration parameter.
Object editors do not support the external property editor feature.
Object list editor
The object list editor allows users to create multiple objects with a pre-defined structure. For example, it could be used for creating a list of person, where each person has a name and address.
The properties of objects that can be created with the editor are defined with itemProperties
parameter. The parameter should contain an array of properties, similar to Inspector configuration array. Another required parameter is titleProperty
, which identifies a property that should be used as a title in Inspector UI. Example configuration:
{
"property": "people",
"title": "People",
"type": "objectList",
"titleProperty": "fullName",
"itemProperties": [
{
"property": "fullName",
"title": "Full name",
"type": "string"
},
{
"property": "address",
"title": "Address",
"type": "string"
}
]
}
The array of properties defined with itemProperties
supports all property types.
The Object List editor type doesn't support default values.
By default the value created by the editor of this type is a non-associative array:
{
"people":[
{"fullName":"John Smith","address":"Palo Alto"},
{"fullName":"Bart Simpson","address":"Springfield"}
]
}
If the result value should be an associative array (object), use the keyProperty
configuration option. The option value should refer to a property that should be used as a key. The key property can use only the string or drop-down editors, its value should be unique and cannot be empty. Example:
{
"property": "people",
"title": "People",
"type": "objectList",
"titleProperty": "fullName",
"keyProperty": "login",
"itemProperties": [
{
"property": "fullName",
"title": "Full name",
"type": "string"
},
{
"property": "login",
"title": "Login",
"type": "string"
},
{
"property": "address",
"title": "Address",
"type": "string"
}
]
}
The login
property in the example above will be used as a key in the result value:
{
"people":{
"john":{"fullName":"John Smith","address":"Palo Alto"},
"bart":{"fullName":"Bart Simpson","address":"Springfield"}
}
}
Set editor
The set editor allows users to select multiple predefined options with checkboxes. Set items can be specified statically with the configuration, using the items
parameter, or loaded dynamically. Example with static items definition:
{
"property": "context",
"title": "Context",
"type": "set",
"items": {
"create": "Create",
"update": "Update",
"preview": "Preview"
},
"default": ["create", "update"]
}
The items
attribute should be a key-value object. If the attribute is not specified, Inspector will try to load options from the server - see Dynamic configuration and dynamic items section above.
The default
parameter, if specified, should be an array listing item keys selected by default.
Set editors do not support the external property editor feature.
Defining the validation rules
Inspector support several validation rules that can be applied to properties. Validation rules can be applied to top-level properties as well as to internal property definitions of object and object list editors. There are two ways to define validation rules - the legacy syntax and the new syntax.
The legacy syntax is supported for the backwards compatibility with existing CMS components definitions. This syntax will always be supported, but it's limited, and cannot be mixed with the new syntax. Example of the legacy syntax:
{
"property": "name",
"title": "Name",
"type": "string",
"required": true,
"validationPattern": "^[a-zA-Z]+$"
"validationMessage": "The Name field is required and can contain only Latin letters.",
}
The legacy syntax supports only two validation rules - required and regular expression. The new syntax is much more flexible and extendable:
{
"property": "name",
"title": "Name",
"type": "string",
"validation": {
"required": {
"message": "The Name field is required"
},
"regex": {
"message": "The Name field can contain only Latin letters.",
"pattern": "^[a-zA-Z]+$"
}
}
}
The key value in the validation
object refers to a validator (see below). Validators are configured with objects, which properties depend on a validator. One property - message
is common for all validators.
required validator
Checks if a value is not empty. The validator can be used with any editor, including complex editors (sets, dictionaries, object lists, etc.). Example:
{
"property": "name",
"title": "Name",
"type": "string",
"validation": {
"required": {
"message": "The Name field is required"
}
}
}
regex validator
Validates string values with a regular expression. The validator can be use only with string-typed editors. Example:
{
"property": "name",
"title": "Name",
"type": "string",
"validation": {
"regex": {
"message": "The Name field can contain only Latin letters",
"pattern": "^[a-z]+$",
"modifiers": "i"
}
}
}
The regular expression is specified with the required pattern
parameter. The modifiers
parameter is optional and can be used for setting regular expression modifiers.
integer validator
Checks if the value is integer and can optionally validate if the value is within a specific interval. The validator can be used only with string-typed editors. Example:
{
"property": "numOfColumns",
"title": "Number of Columns",
"type": "string",
"validation": {
"integer": {
"message": "The Number of Columns field should contain an integer value",
"allowNegative": true,
"min": {
"value": -10,
"message": "The number of columns should not be less than -10."
},
"max": {
"value": 10,
"message": "The number of columns should not be greater than 10."
}
}
}
}
Supported parameters:
-
allowNegative
- optional, determines if negative values are allowed. By default negative values are not allowed. -
min
- optional object, defines the minimum allowed value and error message. Object fields:-
value
- defines the minimum value. -
message
- optional, defines the error message.
-
-
max
- optional object, defines the maximum allowed value and error message. Object fields:-
value
- defines the maximum value. -
message
- optional, defines the error message.
-
float validator
Checks if the value is a floating point number. The parameters for this validator match the parameters of the integer validator described above. Example:
{
"property": "amount",
"title": "Amount",
"type": "string",
"validation": {
"float": {
"message": "The Amount field should contain a positive floating point value."
}
}
}
Valid floating point number formats:
- 10
- 10.302
- -10 (if
allowNegative
istrue
) - -10.84 (if
allowNegative
istrue
)
length validator
Checks if a string, array or object is not shorter or longer than specified values. This validator can work with the string, text, set, string list, dictionary and object list editors. In multiple-value editors (set, string list, dictionary and object list) it validates the number of items created in the editor.
Note: the
length
validator doesn't validate empty values. For example, if it's applied to a set editor, and the set is empty, the validation will pass regardless of themin
andmax
parameter values. Use therequired
validator together with thelength
validator to make sure that the value is not empty before the length validation is applied.
{
"property": "name",
"title": "Name",
"type": "string",
"validation": {
"length": {
"min": {
"value": 2,
"message": "The name should not be shorter than two letters."
},
"max": {
"value": 10,
"message": "name should not be longer than 10 letters."
}
}
}
}
Supported parameters:
-
min
- optional object, defines the minimum allowed length and error message. Object fields:-
value
- defines the minimum value. -
message
- optional, defines the error message.
-
-
max
- optional object, defines the maximum allowed length and error message. Object fields:-
value
- defines the maximum value. -
message
- optional, defines the error message.
-
Inspector events
Inspector triggers several events on the inspectable elements.
change
The change
event is triggered after Inspector applies updated values to the inspectable element. The event is triggered only if the user has changed values in the Inspector UI.
showing.oc.inspector
The showing.oc.inspector
event is triggered before Inspector is displayed. The event handler can optionally stop the process with calling ev.isDefaultPrevented()
. Example - prevent Inspector showing:
$(document).on('showing.oc.inspector', 'div[data-inspectable]', function(ev, data){
ev.preventDefault()
})
The handler could perform any required processing, even asynchronous, and then call the callback function passed to the handler, to continue showing the Inspector. In this case the handler should call ev.stopPropagation()
method to stop the default Inspector initialization. Example - continue showing after some processing:
$(document).on('showing.oc.inspector', 'div[data-inspectable]', function(ev, data){
ev.stopPropagation()
// The callback function can be called asynchronously
data.callback()
})
hiding.oc.inspector
The hiding.oc.inspector
is called before Inspector hiding process starts. The handler can stop the hiding with calling ev.preventDefault()
. Example:
$(document).on('hiding.oc.inspector', 'div[data-inspectable]', function(ev, data){
if (!confirm('Allow hiding?')) {
ev.preventDefault()
}
})
The values entered in Inspector are available through the values
element of the second handler argument:
$(document).on('hiding.oc.inspector', 'div[data-inspectable]', function(ev, data){
console.log(data.values)
})
hidden.oc.inspector
The hidden.oc.inspector
is triggered after Inspector is hidden.