AdminPresentation
EntityForm Building Blocks
- Tabs
- Groups
- Fields
- Collections
Building an EntityForm
The best way to learn how EntityForms are built using AdminPresentation, is to analyze the components of a given EntityForm. For the following examples, we'll take a look at the class-level AdminPresentation defined in the OfferAdminPresentation.java
interface and its associated field-level definitions in OfferImpl.java
. We'll then explore how the EntityForm layout can be modified and extended.
Offer Class-level Annotations
Lets dive straight into exploring the relationship between Tabs and Groups. Here is a shortened version of the AdminPresentationClass
annotation defined in OfferAdminPresentation.java
, which establishes the general structure of the Offer EntityForm:
@AdminPresentationClass(populateToOneFields = PopulateToOneFieldsEnum.TRUE, friendlyName = "OfferImpl_baseOffer",
tabs = {
@AdminTabPresentation(name = OfferAdminPresentation.TabName.General,
order = OfferAdminPresentation.TabOrder.General,
groups = {
@AdminGroupPresentation(name = OfferAdminPresentation.GroupName.Description,
order = OfferAdminPresentation.GroupOrder.Description,
untitled = true),
@AdminGroupPresentation(name = OfferAdminPresentation.GroupName.RuleConfiguration,
order = OfferAdminPresentation.GroupOrder.RuleConfiguration,
untitled = true),
@AdminGroupPresentation(name = OfferAdminPresentation.GroupName.ActivityRange,
order = OfferAdminPresentation.GroupOrder.ActivityRange,
column = 1),
...
}
),
...
@AdminTabPresentation(name = OfferAdminPresentation.TabName.Codes,
order = OfferAdminPresentation.TabOrder.Codes,
groups = {
@AdminGroupPresentation(name = OfferAdminPresentation.GroupName.Codes,
order = OfferAdminPresentation.GroupOrder.Codes,
untitled = true)
}
),
...
}
)
public interface OfferAdminPresentation {
public static class TabName {
public static final String General = "General";
public static final String Marketing = "OfferImpl_Marketing_Tab";
public static final String Qualifiers = "OfferImpl_Qualifiers_Tab";
public static final String Codes = "OfferImpl_Codes_Tab";
public static final String Advanced = "OfferImpl_Advanced_Tab";
}
public static class TabOrder {
public static final int General = 1000;
public static final int Qualifiers = 2000;
public static final int Marketing = 3000;
public static final int Codes = 4000;
public static final int Advanced = 5000;
}
public static class GroupName {
public static final String Description = "OfferImpl_Description";
public static final String ActivityRange = "OfferImpl_Activity_Range";
public static final String Restrictions = "OfferImpl_Restrictions";
public static final String Customer = "OfferImpl_Customer";
public static final String CombineStack = "OfferImpl_Combine_Stack";
...
public static final String Codes = "OfferImpl_Codes_Tab";
public static final String ShouldBeRelated = "OfferImpl_ShouldBeRelated";
}
public static class GroupOrder {
public static final int Description = 1000;
public static final int ActivityRange = 2000;
public static final int Customer = 3000;
public static final int Restrictions = 5000;
...
public static final int Codes = 1000;
public static final int ShouldBeRelated = 1000;
}
public static class FieldOrder {
public static final int Name = 1000;
public static final int Description = 2000;
public static final int Message = 3000;
public static final int TemplateType = 4000;
public static final int Amount = 5000;
public static final int OfferType = 5000;
public static final int DiscountType = 5000;
...
}
}
From a quick skim over these annotations, we can determine the general hierarchy of Tabs and Groups that define the EntityForm. As you can see, these Tab and Group annotations take advantage of predefined constants from the OfferAdminPresentation
interface. Later, we'll see why this is done, but for now, the important thing to note is that Tabs and Groups both define name
and order
properties. This information allows us to place a label on the Tab or Group, and order the elements relative to one another.
You may have also noticed that Groups have a few additional properties. The following Group attributes are Enterprise-only:
untitled
- Determines whether or not the Group's title will be rendered.column
- Determines which column the Group will be placed into. By default, column 0 is the main column and column 1 is the narrow, right-hand-side column.collapsed
- Ifcollapsed = true
, then the Group will be collapsed on page load.
Now, we have an understanding of the general layout of the Offer EntityForm, but we still need to place fields and collections into the form.
Placing Fields & Collections into the EntityForm
Since we've previously defined the Tab/Group structure where each Tab and Group has a unique name, each Field and Collection only needs to identify the name of the Group that it should be placed into. Additionally, each Field and Collection should set the order
property to organize elements within the given Group.
Simple Example
@AdminPresentation(friendlyName = "OfferImpl_Offer_Name",
group = GroupName.Description, order = FieldOrder.Name,
prominent = true, gridOrder = 1,
defaultValue = "New Offer")
protected String name;
@AdminPresentationCollection(friendlyName = "offerCodeTitle",
group = GroupName.Codes, order = FieldOrder.OfferCodes,
addType = AddMethodType.PERSIST)
protected List<OfferCode> offerCodes = new ArrayList<OfferCode>(100);
As you can see in the following screenshots, the name
Field has been placed into the Description
Group (untitled) within the General
Tab.
Additionally, the offerCodes
Collection has been placed into the Codes
Group (untitled) within the Codes
Tab.
Placing a Collection into a Tab
Fields can only be placed into groups, whereas Collections can be placed into Groups or Tabs. The following, is an example of how we could place our offerCodes
collection into the General
Tab:
@AdminPresentationCollection(friendlyName = "offerCodeTitle",
tab = TabName.General,
addType = AddMethodType.PERSIST)
protected List<OfferCode> offerCodes = new ArrayList<OfferCode>(100);
Modifying Existing Layout
So far, we've seen how to place Fields and Collections into Groups and Tabs that were predefined in our class-level annotations. What if we don't like the placement of a particular Field or Collection, and we want to move it? Additionally, what if the desired Group or Tab destination is not specified in the class-level annotations? What options do we have?
XML Overrides
XML Metadata Overrides are the most powerful means of modifying the EntityForm structure for Broadleaf domain. Leveraging these XML entries, we have the ability to modify the AdminPresentation properties for any Field or Collection.
The following is an example of how we would move the name
Field, that we previously discussed, to the Advanced
Group, in the Advanced
Tab:
<mo:overrideItem ceilingEntity="org.broadleafcommerce.core.offer.domain.Offer">
<mo:field name="name">
<mo:property name="group" value="OfferImpl_Advanced" />
</mo:field>
</mo:overrideItem>
If we decided that the name
Field should actually be placed into a new Group named Customer Facing Details
in the General
Tab, we would use the following entries:
<mo:overrideItem ceilingEntity="org.broadleafcommerce.core.offer.domain.Offer">
<mo:group tabName="General" groupName="Customer Facing Details">
<mo:property property="order" value="0"/>
</mo:group>
<mo:field name="name">
<mo:property name="group" value="Customer Facing Details" />
</mo:field>
</mo:overrideItem>
Alternatively, if we wanted the name
Field to go into a new Tab and new Group, we would use the following entries:
<mo:overrideItem ceilingEntity="org.broadleafcommerce.core.offer.domain.Offer">
<mo:tab tabName="New Tab">
<mo:property property="order" value="9999999" />
</mo:tab>
<mo:group tabName="New Tab" groupName="Customer Facing Details">
<mo:property property="order" value="0"/>
</mo:group>
<mo:field name="name">
<mo:property name="group" value="Customer Facing Details" />
</mo:field>
</mo:overrideItem>
For more details on field-level presentation modifications, see the Metadata Overrides doc.
Modifying the EntityForm Leveraging a Domain Extension
Many Broadleaf-based solutions end up extending the out-of-box domain in some way, but don't worry, your EntityForms can easily grow with your domain.
The key item to remember when editing EntityForms for domain extensions is that these extensions inherit AdminPresentationClass data. Therefore, only modifications and additions need to be made to the extension's AdminPresentationClass definition.
NOTE: If a property of a Tab or Group is overridden for out-of-box domain, then the same override will be applied
to any extensions unless extension has its own override.
Just as before, the ultimate goal is to define a Tab/Group hierarchy and then specify where each Field or Collection should be placed in that hierarchy.
Tab/Group Overrides on a Domain Extension
To override the properties of an inherited Tab or Group definition, we would leverage the tabOverrides
or groupOverrides
properties of the extension's AdminPresentationClass annotation.
The following is an example of how we would override the name
of the Codes
Tab and the order
of the Advanced
Group:
@AdminPresentationClass(
tabOverrides = {
@AdminTabPresentationOverride(tabName = OfferAdminPresentation.TabName.Codes,
property = PropertyType.AdminTabPresentation.NAME,
value = "Offer Codes")
},
groupOverrides = {
@AdminGroupPresentationOverride(tabName = OfferAdminPresentation.TabName.Advanced,
groupName = OfferAdminPresentation.GroupName.Advanced,
property = PropertyType.AdminTabPresentation.ORDER,
value = "9999999")
}
)
Adding Tab or Group for Domain Extension
The following is an example of how we would establish a new Tab and Group, and a new Group in a inherited Tab:
@AdminPresentationClass(
tabs = {
@AdminTabPresentation(name = "NEW_TAB", order = 1,
groups = {
@AdminGroupPresentation(name = "NEW_GROUP", order = 1)
}
),
@AdminTabPresentation(name = OfferAdminPresentation.TabName.Codes,
groups = {
@AdminGroupPresentation(name = "NEW_GROUP_IN_INHERITED_TAB", order = 1)
}
)
}
)
Best Practices
- Moving the AdminPresentationClass annotation to an interface
- In many cases, the AdminPresentationClass annotation can grow to a fairly significant size if the domain object has a large amount of fields. Because of this, we decided that moving the annotation to its own interface that the domain object would implement. The main intent here is to avoid cluttering the domain definition.
- Tab & Group Constants
- We found that by introducing subclasses that contain the necessary Tab and Group constants to the domain definition's AdminPresentation interface, we were able to avoid repetition of the values and reduce the potential for bugs.
- Only use the
group
&order
properties on fields- While it is possible to use the
group
,groupOrder
,tab
, andtabOrder
properties to create new EntityForm Tabs or Groups, this is merely intended for backwards compatibility. We prefer to define Tabs and Groups at the class-level to avoid ambiguity of situations where two Fields may have the sametab
(name) property but differenttabOrder
properties.
- While it is possible to use the
- When extending an entity, consider updating the AdminSection's ceiling entity or creating a new AdminSection to manage the entity.
Possible Hangups
- AdminPresentationClass annotation on class & interface
- The AdminPresentationClass processor will first look at the desired class, then move to any associated interfaces looking for the AdminPresentationClass annotation, and it will always use the first one that it finds. Therefore, if your Tab/Group structure is not being built properly, make sure that the AdminPresentationClass annotation is only present once for a given level of extension.
- Key vs processed value
- The out-of-box TabName and GroupName constants generally use values like
OfferImpl_Description
andOfferImpl_Activity_Range
which are actually keys for display values stored in a*.properties
file. When the EntityForm is rendered, the Thymeleaf view processor will retrieve the necessary display values from the*.properties
files. For the purpose of associating Fields/Collections to Tabs/Groups, these property keys are the values that you should use.
- The out-of-box TabName and GroupName constants generally use values like
- Group/Tab not defined in the class-level structure
- If you find that a field is unexpectedly being placed into a group under the
General
tab, then it is likely the case that you have not correctly targeted or defined the Group.
- If you find that a field is unexpectedly being placed into a group under the