Proposal: Conditional Rendering For Editor Properties Form
See original GitHub issueDemand Overview
In Some situations, the component lib developers want to make the component property form whether to render depends on another property value.
Such as the Table
Component, the buttonConfig
property should only render when the column’s type
is button
.
Scheme Design
Maybe there are three ways to implement this demand:
- Use
Type.Union
to define the properties spec - Add a new field to properties spec options to decide the property form whether should render
- Let component lib developers use custom widgets to render the properties form
Type.Union
Currently, we already can use Type.Union
to define property spec to make the form rendering depends on its value.
{
"type": Type.Union(
[
Type.String({
unionValue: 'text'
}),
Type.Object({
buttonConfig: Type.Object({
text: Type.String({
title: 'Text',
}),
handlers: Type.Array(EventHandlerSchema),
}, {
title: 'Button Config',
unionValue: 'button'
}),
}),
]
)
}
But this spec would break the type checking because the TypeScript type of type
in the above example would be:
type T = string | { buttonConfig: {...} };
So, Maybe this way doesn’t work?
New Field For Conditional Rendering
We can add a new field to spec options to describe when to render this property form. The type definition is:
interface Condition {
// the key of the dependent property
dependsOn: string;
// rendering form when the dependent property value is equal to this value
value: string;
}
type UnionConditions = Condition[];
type IntersectionConditions = Condition[];
And the example specs are just like these:
// render when: type === 'text' || type === 'button'
{
"text": Type.String({
"unionConditions": [{
"dependsOn": "type",
"value": "text"
}, {
"dependsOn": "type",
"value": "button"
}]
})
}
// render when: type === 'text' && enable === true
{
"text": Type.String({
"intersectionConditions": [{
"dependsOn": "type",
"value": "text"
}, {
"dependsOn": "enable",
"value": true
}]
})
}
But, how to design the dependsOn
is a question, we should split it into different situations to talk about it.
The Related Properties Are Under The Same Level
When the related properties are under the same level, it would be simple to design the properties spec. The dependsOn
is set to the key of another property which is under the same level just alright. Such as:
{
"columns": Type.Array(
Type.Object({
...,
"type": Type.KeyOf(...),
"buttonConfig": Type.Object({...}, {
"unionConditions": [{
"dependsOn": "type",
"value": "button"
}]
})
})
)
}
The Related Properties Are Cross The Different Levels
We can set the absolute path to describe which property to depend on. Just like:
{
"columns": Type.Array(
Type.Object({
...,
"type": Type.KeyOf(...),
"buttonConfig": Type.Object({...}, {
"unionConditions": [{
"dependsOn": "columns.type",
"value": "button"
}]
})
})
)
}
But the above spec has a problem here. You may notice that if a property is Array
type, it would have the same sub-properties key between different array items, but which property’s value should we actually depends on?
I think we should define some rules for the Array
type properties when depending on it.
- Only allow depending on the property which in the same array item
- Not allow depending on the property under the other
Array
type properties
Custom Widget
We will support registering the custom widgets for properties form later, and then the component lib developers can freely implement the form widgets that they want.
However, if the related properties for conditional rendering aren’t under the same level, the component lib developers have to implement a large widget to achieve this.
In addition, if a component lib has many properties which only need to render the form depending on different conditions, they have to implement many different widgets for them. That is awful for the component lib, so we better support the common way for the condition rendering.
Summary
There are three ways that we mention above to implement conditional rendering for properties form, let’s take a summary for them.
In conclusion, I think the second scheme is better.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:10 (9 by maintainers)
Yes, it’s flexible. We can support more operators incrementally in the future and don’t worry to make a breaking change. If we want to add more operators, we can add the operators just like this:
Component developers, only need to follow the design to write JSON just can implement the conditional rendering. In addition, we also export the TypeScript type definition to help the component developers to write the conditional JSON.
The editor needs to implement a function to parse the conditional JSON to real logic execution and another function to get the real value by the path.
Maybe we should return the
false
condition and hide the form widget when the conditional JSONs are invalid?Oh, looks like the JSON schema also have a conditional semantic built-in: if-then-else