Web components
WebCmsComponentModel
WebCmsComponentModelService
and WebCmsComponentModel
WebCmsComponentType
The persisted entity is implemented by WebCmsComponent
, but usually you will want to work with the WebCmsComponentModel
.
The latter represents the entire strongly typed model ready for rendering, whereas the former only has the raw attributes.
converted to a WebCmsComponent
by a WebCmsComponentModelWriter
converted from a WebCmsComponent
by a WebCmsComponentModelReader
Default component types
WebCmsModule contains a number of default component types, backed by a set of default WebCmsComponentModel
implementations.
You can easily define your own component types by extending one of these base types, see the chapter Defining component types.
The following component types are added with the default data:
Type key | Description | Base type 1 | WebCmsComponentModel |
---|---|---|---|
proxy |
A proxy for another component that will be rendered instead. |
proxy |
|
placeholder |
Represents a placeholder for content provided by code or templates. |
placeholder |
|
container |
Container of multiple ordered components. The user can add or remove child components to the container. |
container |
|
fixed-container |
Container of a fixed set of components. The user can not add or remove child components to the container. |
fixed-container |
|
text-field |
A single line of plain text. |
plain-text |
|
plain-text |
Plain text component. |
plain-text |
|
rich-text |
Rich text component - supporting HTML markup and default assets if enabled by other modules. |
rich-text |
|
html |
A snippet of HTML. |
markup |
|
image |
Represents a |
image |
|
1 The Base type is the value of the type attribute on the WebCmsComponentType
. Every component type must have a unique type key, but several component types can have the same base type.
See WebCmsComponentUtils
for some utility functions when working with components and component types.
WebCmsComponentModel implementations
All component models must extend WebCmsComponentModel
.
A number of default implementations is provided by the WebCmsModule.
It is usually the WebCmsComponentType
base type (type attribute) that will determine the WebCmsComponentModel
that gets instantiated.
TextWebCmsComponentModel
Represents a text component that can be either plain text, rich text or markup. In all cases an optional profile is supported that can indicate the sub-types of content that are supported.
A TextWebCmsComponentModel
is a large body of text (the content
property) that should be escaped or not upon rendering.
The other properties are mostly relevant for the administrative user interface.
TextWebCmsComponentModel
content can contain content markers.
The following base types will create a TextWebCmsComponentModel
:
-
plain-text: will escape its content when rendering
-
rich-text: unescaped content with a RTE in the administration UI
-
markup: unescaped content with a HTML editor in the administration UI
TextWebCmsComponentModel
implementations check extra WebCmsComponentType
attributes to map their properties.
Attribute | Description |
---|---|
multiLine |
If set to false, the component will behave as a single line of text. In all other cases it will behave as a multi-line text component. |
rows |
Size indicator: number of rows the component should have by default. Only applicable in case of a multi-line component. |
profile |
Optionally specify an additional text profile this component should use. The text profile can for example be used to determine settings of the rich text editor. |
ContainerWebCmsComponentModel
A ContainerWebCmsComponentModel
represents an ordered collection of other components (the container members).
By default a container simply renders its individual members in order.
Containers can optionally also support markup (determined by the supportsMarkup attribute on the actual WebCmsComponentType
).
If markup is set, it represents the rendering template for the container and members should be rendered using content markers.
Within container markup, component content markers can use the container scope to reference a member of the container itself.
The following base types will create a ContainerWebCmsComponentModel
:
-
container: allows dynamic addition/removal of members
-
fixed-container: holds a fixed set of members (useful as a base type for custom components)
ContainerWebCmsComponentModel
implementations check extra WebCmsComponentType
attributes to map their properties.
Attribute | Description |
---|---|
supportsMarkup |
If set to true then markup on the container will be used for rendering if there is markup set. |
PlaceholderWebCmsComponentModel
A PlaceholderWebCmsComponentModel
has only a single relevant property: the name of the placeholder whose content should be rendered.
By default if no placeholder content is available, this component will render an empty string.
Base type: placeholder
ProxyWebCmsComponentModel
A ProxyWebCmsComponentModel
holds a reference to another component (the proxy target) and renders that component instead.
Use a ProxyWebCmsComponentModel
if you want to add an already existing component as a member of a container.
Base type: proxy
ImageWebCmsComponentModel
An ImageWebCmsComponentModel
holds a reference to a WebCmsImage
asset.
Default rendering of an ImageWebCmsComponentModel
is not very useful and developers should provide their own rendering by either updating the WebCmsComponentType
with a custom template or by providing the rendering infrastructure (see Thymeleaf rendering).
For the ImageWebCmsComponentModel to function, a valid WebCmsImageConnector bean should be present. See the chapter Image model for more information.
|
Activating component support
Any WebCmsObject
- this includes all assets and components themselves - can have web components linked to it.
Enabling components for WebCmsObject implementations: wire WebCmsObjectComponentViewsConfiguration: registerComponentsAssociation
Rendering components
Creating shared web components
Name | Type | Mandatory | Description | Default Value |
---|---|---|---|---|
Component Type |
Component Type |
Yes |
Type of the component, selectable from the list of predefined component types. |
None |
Title |
String |
Yes |
The name of the component. Displayed in the admin section. |
None |
Name |
String |
Yes |
The unique identifier with which you can approach the component in your Thymeleaf template. Should be unique, and can be auto-generated based on Title |
None |
Using components in your controllers
A WebCmsComponentModelHierarchy
is attached to the ApplicationContext
of the current request and allows components to be looked up in the various scopes. By default the global
and domain
scope are registered. Using the WebCmsComponentModelHierarchy
, it is possible to register additional scopes, attach/replace/remove components to a scope and define aliases for a scope.
The WebCmsComponentModelHierarchy
is used to retrieve components in the thymeleaf dialect and is used to search for components in various scopes.
Note: The WebCmsComponentModelHiercharchy
is a request scoped bean, so it can simply be autowired. It should however not be wired inside a controller handler method as it has a parameterless constructor.
Example of registering a custom component to the asset
scope:
@Controller
@RequiredArgsConstructor
public class MyController {
private final WebCmsComponentModelHierarchy componentModelHierarchy;
@WebCmsArticleMapping(articleType="blog")
public void myHandler(){
WebCmsComponentModelSet componentModelSet = new WebCmsComponentModelSet();
TextWebCmsComponentModel text = new TextWebCmsComponentModel();
text.setContent("some text");
text.setName("my-custom-component");
componentModelSet.add(text);
componentModelHierarchy.registerComponentsForScope(componentModelSet, "asset");
}
}
Placeholders
WebCmsPlaceholderContentModel
Content markers
WebCmsModule provides content marker infrastructure to work with special marker snippets inside textual content. Upon rendering, these markers would be processed and the resulting output would be rendered instead.
Sample content markers
Insert component content: @@wcm:component(title,global,false)@@
Insert placeholder content: @@wcm:placeholder(myplaceholder)@@
Two default markers are supported:
-
placeholder content markers indicate placeholder content should be rendered at the marker’s location
-
component content markers indicate a component should be rendered at the marker’s location
A WebCmsContentMarker
consists of 2 parts:
-
a key indicating the type of content marker - the key is required and cannot contain whitespace
-
an optional string of parameters that is supported by that type
In text a content marker is always surrounde by @@
.
-
@@my.marker@@
-
key: my.marker
-
-
@@my.marker(my, params)@@
-
key: my.marker
-
parameter string: my, params
-
The WebCmsContentMarkerService
is the service for working with content markers in general.
Default rendering support is available for Thymeleaf templates, see the WebCmsModule Thymeleaf dialect.
Content markers are supported in all default component types.
Placeholder content markers
A placeholder content marker indicates that placeholder content should be rendered at the marker’s location. The key is wcm:placeholder and the parameter is the name of the placeholder whose content should be rendered.
Example: @@wcm:placeholder(some.placeholder)@@
Component content markers
A component content marker indicates a component should be rendered at the location of the marker. The key is wcm:component and 3 parameters are required:
-
the component name
-
the scope in which to look for the component
-
should parent scopes be searched as well: true/false
Whitespace is not supported in the parameter string.
Examples:
-
@@wcm:component(my.component.name,default,true)@@
-
will search for my.component in the default scope and all parent scopes
-
-
@@wcm:component(my.component.name,global,false)@@
-
will search for my.component in the global scope only (as global is the last scope the parent flag is of no meaning)
-
-
@@wcm:component(my.component.name,container,false)@@
-
will search for my.component in the container members - only relevant in the context of a
ContainerWebCmsComponentModel
being rendered
-
Creating custom content markers
See WebCmsModule Thymeleaf dialect on how to add content marker support for component rendering.
Defining component types
You can easily define your own component types. Depending on your needs you can extend one of the base types or create a fully custom implementation.
Extending the base types
You can add a custom WebCmsComponentType
by importing it via YAML or by adding it via the WebCmsComponentTypeRepository
.
Component types to import should be defined in the section types - component where the key of a component type definition is the`unique WebCmsComponentType
typeKey
.
types:
component:
my-component:
name: Custom component
description: Custom HTML component with additional metadata.
attributes:
type: markup
metadata: "my.component.MyMetadata"
A WebCmsComponentType
has a collection of attributes.
This is a map of String
key/value pairs.
Adding attributes is a way to provide processing metadata without having to create custom WebCmsComponentType
implementations.
All base types support 3 default attributes that pack a lot of punch in creating custom component types:
Attribute name | Description |
---|---|
type |
Contains the base type for the component type. Examples: container, markup … |
template |
Optional: identifier of the (Thymeleaf) template that should be used for rendering the resulting Example: th/mymodule/mytemplate :: fragment |
metadata |
Optional: name of the class that should be used for the component metadata. Example: my.module.components.MyComponentMetadata |
Any component type can have a template attribute set that identifies the (Thymeleaf) template that should be used for rendering the component instances.
If a template is specified it will always be used for rendering and any other WebCmsComponentModelRenderer
for that component type will be skipped.
You can customize rendering the default WebCmsComponentModel
implementations just by specifying a custom template.
Property renderTemplate on WebCmsComponentModel allows you to set a custom template per-component instead of per component type.
If a renderTemplate is set on the component itself, it will take precedence over the WebCmsComponentType template attribute.
Setting renderTemplate is not enabled by default in the administration UI.
|
Any WebCmsComponentModel
has a metadata
property that refers to an object that has all custom metadata for a component.
The type of metadata object is configured by setting the class name as the metadata attribute on the WebCmsComponentType
.
The metadata instance will always be created as a prototype using the BeanFactory
and as such supports autowiring other beans from the ApplicationContext
.
A metadata class must:
-
be serializable to and from JSON using Jackson
ObjectMapper
-
Jackson annotations on the metadata class are supported if necessary to define (de-)serializer rules
-
special care should be taken not to serializer any beans that might have been wired
-
-
implement a valid
equals()
method in order to have change detection and better import/export performance -
be easily cloneable:
-
have either a no-arguments public constructor and provide setters/getters for all relevant properties
-
OR implement
EntityWithDto
orCloneable
-
If the administration UI is active, a form for metadata properties will automatically be generated using the EntityModule.
This does require you to register the metadata type as an entity in the EntityRegistry
.
Default (de-)serialization for WebCmsObject implementations is provided by the WebCmsModule by dispatching to the WebCmsDataConversionService .
This means metadata properties can often refer to other web cms types without requiring custom configuration.
|
See creating a custom component type if you want to have a custom metadata class, change serialization or admin UI rendering.
When defining a custom component type, you can link a componentTemplate component to the WebCmsComponentType
definition.
If present, that component will be used as a template for a new component of your custom component type.
Class properties as well as any child components will be copied to a new component your type.
When your component type and componentTemplate are a container type, any TextWebCmsComponentModel
member of the template component can have special markers in its content.
These markers will be replaced when copying the template to a newly created component:
-
@@container.name@@
will be replaced with thename
of the newly created container -
@@container.title@@
will be replaced with thetitle
of the newly created container
The componentTemplate should have the same class implementation as the component type being defined.
For example when creating a fixed-container, the componentTemplate must implement ContainerWebCmsComponentModel .
Providing custom metadata is not supported on the template component, the metadata will be reset when creating a new component.
|
Example: custom container component
This is an example of a custom component that is a fixed container with 2 member components. A custom metadata class allows configuring the position of the members (left or right), the custom template uses the metadata to determine what the output should be.
types:
component:
left-right:
name: Left or Right
description: Renders 2 components in a specific layout.
attributes:
type: fixed-container
template: "th/mymodule/components :: left-right"
metadata: "mymodule.LeftRightMetadata"
wcm:components:
componentTemplate:
componentType: container
wcm:components:
one:
componentType: rich-text
title: One
sortIndex: 1
body: "@@container.name@@"
two:
componentType: rich-text
title: Two
sortIndex: 2
body: "@@container.title@@"
When creating a new left-right component the members of componentTemplate will be cloned into the new container. The text components one and two will get their default content set with respectively the name and title of the new left-right component.
The sortIndex will determine the order of the components in the administration UI.
@Data // Use Lombok @Data to generate getters, setters and equals() method
public class LeftRightMetadata
{
enum Layout
{
LEFT_TO_RIGHT,
RIGHT_TO_LEFT
}
/**
* Determines the order of rendering one and two.
*/
@NotNull
private Layout layout = Layout.LEFT_TO_RIGHT;
}
// Register the metadata class as an entity as to activate the administration UI
@Configuration
@ConditionalOnAdminUi
class LeftRightConfiguration implements EntityConfigurer {
@Override
public void configure( EntitiesConfigurationBuilder entities ) {
entities.create().entityType( LeftRightMetadata.class, true );
}
}
The metadata only has a single property layout
.
When the administration UI is active (presence of EntityModule and AdminWebModule) the layout value can be selected in the user interface.
The default administration UI uses the the EntityModule to build the metadata form, so we register the LeftRightMetadata
as an entity.
If you do not want to use the EntityModule to generate the metadata form, you can provide a custom WebCmsComponentModelMetadataAdminRenderer .
See create a custom component type for more information.
|
<th:block th:fragment="left-right(component)"
th:with="metadata=${component.metadata}">
<section th:if="${metadata.layout.name() eq 'LEFT_TO_RIGHT'}">
<div class="left"><across:view element="${component.getMember('one')}" /></div>
<div class="right"><across:view element="${component.getMember('two')}" /></div>
</section>
<section th:if="${metadata.layout.name() eq 'RIGHT_TO_LEFT'}">
<div class="left"><across:view element="${component.getMember('two')}" /></div>
<div class="right"><across:view element="${component.getMember('one')}" /></div>
</section>
</th:block>
The Thymeleaf template inspects the metadata layout
property and renders members one and two in a fixed location.
As a WebCmsComponentModel
is a ViewElement
using an across:view node takes care of rendering the member components.
Filtering selectable WebCmsComponentType options
To filter the provided dropdown options for a WebCmsComponent, the user can perform two actions:
-
options that should never be present by default
-
selected set of options that should be present.
These actions are provided through the DefaultAllowedComponentTypeFetcher
. Should you like to provide your own logic to filter the selectable options, for one or more types, you will have to provide your own implementations of the WebCmsAllowedComponentTypeFetcher
interface.
Non-selectable WebCmsComponentTypes
To prevent a WebCmsComponentType
from showing up in the list unless explicitly specified, the user has to provide the component type with the componentRestricted
attribute, with its value set to true. This ensures that unless the WebCmsComponentType is specified as an option for the WebCmsObject
through a WebCmsTypeSpecifierLink
, it will never be shown as an option.
Examples of non-selectable WebCmsComponentTypes are the default placeholder, proxy and fixed-container component types.
types:
component:
fixed-container:
objectId: "wcm:type:component:fixed-container"
name: Fixed container
description: Container of a fixed set of components.
attributes:
type: fixed-container
componentRestricted: true
-
A fixed-container should never be available as a selectable option unless specified (through a
WebCmsTypeSpecifierLink
)
Selectable WebCmsComponentType options
In the case where a component type may only contain other specific component types, the type should be provided by the childComponentsRestricted
attribute, with its value set to true. The provided component type options will then be equal to the specified WebCmsTypeSpecifierLink
links with link type allowed-component.
Example
types:
component:
partner-container:
name: Partners
attributes:
type: container
childComponentsRestricted: true
wcm:types:
- typeSpecifier: "wcm:type:component:partner"
linkType: allowed-component
-
A partner-container should only contain components of the type partner
Custom component type
Apart from extending one of the base types and using a custom template or metadata class, you can also pretty much customize any part of the component related infrastructure by providing specific interface implementations. This allows you to create a fully custom component type and read/write/render or manage it in whichever way you like.
The following list of the component related interfaces and their role:
Interface | Description |
---|---|
|
Converts from a |
|
Saves a |
|
Renders a |
|
Provides a |
|
Provides a |
|
Provides a |
|
Builds a |
All component related interfaces use the same processing approach:
-
all beans of that type are detected and ordered
-
a
supports()
method is used to check if the bean applies for a certain component -
the first bean that applies will be used
As the implementations are always queried in order, customizing an implementation is a matter of:
-
providing your implementation as a bean
-
implementing
supports()
to match for all component types you want -
ensure your bean is ordered (use
@Order
or implementOrdered
) before any other that might also apply for that component type
Customizing the components administration UI
WebCmsModule provides a default administration UI built on AdminWebModule and EntityModule.
The default administration UI uses a form of tabs to display the different sections (eg. content, members, metadata) of a component. Only sections relevant for the component type will be shown to the user.
Administration UI labels and descriptions
The default administration UI for components supports configuring the labels. See the message codes overview.
Creating custom component forms
You can customize the forms being rendered by providing custom xAdminRenderer
implementations. This allows you to build
Configuring rich-text and markup components
WebCmsModule supports rich text components and HTML components out of the box, using TinyMCE and CodeMirror respectively. Rich-text components are identified with the base type rich-text
, markup with markup
.
rich-text:
objectId: "wcm:type:component:rich-text"
name: Rich text
description: Rich text component - supporting HTML markup and default assets if enabled by other modules.
attributes:
type: rich-text
profile: rich-text
html:
objectId: "wcm:type:component:html"
name: HTML
description: A snippet of HTML.
attributes:
type: markup
In addition to the base type, a component type can define a profile attribute value, denoting a custom profile that should be used to initialize the editor for that component type.
The default rich-text component type uses a profile called rich-text
as well.
You can customize the profile configuration using Javascript. This is done by registering profiles on the WebCmsComponentProfileRegistry
. A profile registration requires the following three attributes:
-
componentType: this is the base type of the component (eg.
rich-text
ormarkup
) -
profileName: a unique profile name
-
profile: JSON object that holds the actual profile data (for example the TinyMCE configuration object)
Profiles can be inherited. If your profile data has a _parentProfile attribute, the value is expected to be the name of another profile for the same component type. The final profile will be the data of the parent profile merged together with the requested profile. Values from the parent will be replaced by values from the requested profile. Every profile can have a (optional) single parent.
(function ( $ ) {
/**
* Limited rich-text profile: inherit from the _base profile
* and only show bold/italic buttons.
*/
WebCmsComponentProfileRegistry.registerProfileForComponentType(
'rich-text', /* component type */
'limited', /* profile name */
{
_parentProfile: '_base',
toolbar1: 'bold italic'
}
);
})(jQuery);
Custom profiles must be registered in Javascript. This usually happens before initialization of form elements (as the profiles should already be registered when that happens), but it must happen after the inclusion of the TextWebCmsComponentAdminResources package and in the javascript page end phase. The reason for this is that the profile registry and the default profiles are defined during JAVASCRIPT_PAGE_END of the WebCmsModule resources.
|
Default profiles
The following default profiles are available:
Component type | Profile name | Usage |
---|---|---|
|
|
Base profile for technical configuration of TinyMCE. Sets up things like the image picket configuration, toolbar behaviour etc. It’s probably best to have your custom profiles inherit from this one. |
|
|
Default profile for a TinyMCE editor: configures default plugins and toolbars. Extends |
|
|
Actual profile used by the default Rich text component. Inherits from |
|
|
Default CodeMirror configuration used by the HTML component. Inherits from a |
Detecting tab switching
Sometimes tab switching needs to be detected in order to re-render the client-side interface. Any element that has data attribute data-wcm-component-refresh set will receive the wcm:componentRefresh event whenever a component tab is being activated.
// Example refreshing the RTE when a component tab is being switched
$( '[data-wcm-markup-type=markup]', node ).each( function () {
var cm = CodeMirror.fromTextArea( $( this )[0], {} );
$( this ).on( 'wcm:componentRefresh',
function () {
cm.refresh();
} )
.attr( 'data-wcm-component-refresh', 'true' ); // ensure we receive the event
} );
Importing components
Single value importing
The TextWebCmsComponentModel
and ImageWebCmsComponentModel
support updating existing components using a single attribute value.
This is the same as assigning the following property:
Component type | Property |
---|---|
|
content |
|
image |
assets:
article:
- title: My article
wcm:components:
body:
content: Update the article body text
assets:
article:
- title: My article
wcm:components:
body: Update the article body text