Conditionals.when("type").equalTo("PRODUCT");
Conditionals are used throughout the admin metadata to gate certain components and functionality. Form components are able to use conditionals to only render when the form state matches a certain state. Request mappings are able to use conditionals to only map or transform data when the data matches a certain state.
Property conditionals can be used to condition functionality based on the state of properties.
Property conditionals include the following properties:
type
The type of the conditional. PROPERTY
for property conditionals.
field
The name of the property the conditional targets.
operator
The operator which decides how to evaluate the targeted field.
For example, ==
would mean an equality check.
value
The value to compare the targeted field against. operator
enumerations like EXISTS
do not require this.
Let’s take a look at an example of a property conditional which matches when evaluated against data with a type
property equal to PRODUCT
in the Java DSL and JSON formats:
Conditionals.when("type").equalTo("PRODUCT");
{
"type": "PROPERTY",
"field": "type",
"operator": "==",
"value": "PRODUCT"
}
If we evaluated this conditional against the following JSON, we would expect it to match:
{
"type": "PRODUCT"
}
Alternatively, if we evaluated this conditional against the following JSON, we would expect it to not match:
{
"type": "VARIANT"
}
Property conditionals can be a useful way to conditionally render fields within a form, for example:
form.addField("description", Fields.string()
.label("Description")
// only renders when "hasDescription" is true
.conditional(Conditionals.when("hasDescription").equalTo(true)))
Logical conditionals can be used to provide logical operators between a set of condiionals.
Logical conditionals include the following properties:
type
The type of the conditional. LOGICAL
for logical conditionals.
operator
The operator of the conditional. One of AND
, OR
, or NOT
.
conditionals
The set of conditionals to evaluate and apply the logical operator between.
Let’s take a look at an example of a logical conditional which matches when either type
is PRODUCT
or VARIANT
:
Conditionals.or(
Conditionals.when('type').equalTo('PRODUCT'),
Conditionals.when('type').equalTo('VARIANT')
);
{
"type": "LOGICAL",
"operator": "OR",
"conditionals": [
{
"type": "PROPERTY",
"field": "type",
"operator": "==",
"value": "PRODUCT"
},
{
"type": "PROPERTY",
"field": "type",
"operator": "==",
"value": "VARIANT"
}
]
}
If we evaluated this conditional against the following JSON, we would expect it to match:
{
"type": "PRODUCT"
}
Alternatively, if we evaluated this conditional against the following JSON, we would expect it to not match:
{
"type": "CATEGORY"
}
Collection conditionals can be used to condition functionality based on the state of members of a collection property.
Collection conditionals include the following properties:
type
The type of the conditional. COLLECTION
for colleection conditionals.
field
The name of the collection property the conditional targets.
conditionals
The set of conditionals to evaluate against each collection member.
matchStrategy
The strategy used to determine how matches of collection members affect the final match.
For the ALL
strategy, all members must match the conditionals. For the ANY
strategy, only one member must match the conditionals.
matchIfEmpty
Whether an empty collection should be considered a match. If true, a null
, undefined
, or empty collection will be considered a match.
Let’s take a look at an example of a collection conditional which matches when all collection members include a type
property equal to PRODUCT
in the Java DSL and JSON formats:
Conditionals.whenCollection("choices")
.allMatch()
.notMatchIfEmpty()
.conditional(Conditionals.when("type").equalTo("PRODUCT"));
{
"type": "COLLECTION",
"field": "choices",
"matchStrategy": "ALL",
"matchIfEmpty": false,
"conditionals": [
{
"type": "PROPERTY",
"field": "type",
"operator": "==",
"value": "PRODUCT"
}
]
}
If we evaluated this conditional against the following JSON, we would expect it to match:
{
"choices": [
{
"type": "PRODUCT"
}
]
}
Alternatively, if we evaluated this conditional against the following JSON, we would expect it to not match:
{
"choices": [
{
"type": "VARIANT"
}
]
}
Collection conditionals can be a useful way to conditionally render fields within a form, for example:
form.addField("defaultProductId", createProductLookup()
// only renders when each "choice" has a "type" of "PRODUCT"
.conditional(Conditionals.whenCollection("choices")
.allMatch()
.notMatchIfEmpty()
.conditional(Conditionals.when("type").equalTo("PRODUCT")))
The admin supports the introduction of new conditional types if the out-of-box ones do not support a use case.
The first step to adding a new conditional type is within the frontend application. We will need to import the conditional helpers and register a new conditional provider within our frontend code. Let’s take a look at a conditional type that matches when a targeted field is lower-case:
import { services } from '@broadleaf/admin-components';
import { get } from 'lodash';
services.ConditionalHelper.registerConditionalProvider({
order: 5000,
canHandle(conditional) {
// if type matches "IS_LOWERCASE", this provider will be engaged
return conditional.type === 'IS_LOWERCASE';
},
evaluate(conditional, values, helper) {
const value = get(values, conditional.field);
// if the targeted value is a "string" and equals itself lower-cased, we consider it a match
return typeof value === 'string' && value === value.toLowerCase();
}
});
The second step to adding a new conditional type is within the metadata DSL.
We will need to create a new interface and implementation class for the new conditional type.
Let’s take a look at doing this for our previous IS_LOWERCASE
example.
First, we need to create an interface IsLowercaseConditional
:
// IsLowercaseConditional.java
public interface IsLowercaseConditional<C extends IsLowercaseConditional<C>> extends Conditional<C> {
String TYPE = "IS_LOWERCASE";
@Override
default String getType() {
return TYPE;
}
String getField();
void setField(String field);
default C field(String field) {
setField(field);
return self();
}
}
Next, we need to create an implementation, DefaultIsLowercaseConditional
:
@Getter
@Setter
@EqualsAndHashCode
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DefaultIsLowercaseConditional implements Serializable,
IsLowercaseConditional<DefaultIsLowercaseConditional> {
private static final long serialVersionUID = 1L;
/**
* Field name target for this conditional
*/
private String field;
public DefaultIsLowercaseConditional(String field) {
setField(field);
}
public DefaultIsLowercaseConditional(IsLowercaseConditional<?> conditional) {
setField(conditional.getField());
}
@Override
public DefaultIsLowercaseConditional copy() {
return new DefaultIsLowercaseConditional(this);
}
}
Note
|
We create both an interface and implementation class to ensure our new conditional type is extensible in the event we want a different implementation. |
Finally, since we have the frontend code and DSL complete, we can make use of our new conditional:
form.addField("description", Fields.string()
.label("Description")
// only render this field when "name" is lower-case
.conditional(new DefaultIsLowercaseConditional("name")));