Components

Contents

Any content item may be configured with a presentation - in the form of a page. Pages are editorially composed from one or more components.

Regions

Regions are not strictly components themselves, but enable placement of components, kind of like a folder. Regions have a fixed name, that is known to the component the region belongs to.

In Content Studio, regions are visually displayed as "dropzones" for components.

To exist, regions must actually be defined within another component. This definition is placed within the application code. Only page and layout components support regions.

Page

The page component is always the root entry of any component structure. A page component may define zero, one, or multiple regions. Each region must have a unique name.

Conventionally, pages define a single region called "main".

To create a page component you must create the following files and folders in your project structure:

page component project structure
/src/
  main/
    resources/
      site/
        pages/
          <page-name>/
            <page-name>.js
            <page-name>.xml

Replace <component-name> with your preferred name.

The name you choose for a component will be used in the underlying data of the stored page. So choose your name wisely.

Page descriptor

The xml file placed in the component structure serves several purposes.

Example page descriptor
<page>
  <display-name i18n="component.page.name">My first page</display-name> (1) (2)
  <description>Front page of our site</description>  (3)
  <form/>  (4)
  <regions>
    <region name="main"/>  (5)
  </regions>
</page>
1 display-name provides a display name used by the editorial interface
2 display-name/i18n optionally specify localization key
3 description Description field shown when creating a part in content studio
4 form allows the definition of a configuration form based on the schema system
5 region optionally specify regions for the page

Page controller

A page controller handles requests to the page. The controller is a required file written in JavaScript and must be named [page-name].js. A controller exports a method for each type of HTTP request that should be handled. The handle method has the request object as a parameter and returns the response object.

Example page controller
// Handles a GET request
exports.get = function(req) {}

// Handles a POST request
exports.post = function(req) {}

Here’s a simple controller that acts on the GET request method.

Example simple get request
exports.get = function(req) {

  return {
    body: '<html><head></head><body><h1>My first page</h1></body></html>',
    contentType: 'text/html'
  };

};

Render-view

If you feel like concatenating strings to create an entire web page is a little too much hassle, Enonic XP also supports views. A view is rendered using a rendering engine; we currently support XSLT, Mustache and Thymeleaf rendering engines. This example will use Thymeleaf.

To make a view, create a file my-first-page.html in the view folder.

Example view file
<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <h1>My first page, with a view!</h1>
  </body>
</html>

In our [page-name].js file, we will need to parse the view to a string for output. Here is where the Thymeleaf engine comes in. Using the Thymeleaf rendering engine is easy; here is how we do it.

Example controller with thymeleaf
var thymeleaf = require('/lib/thymeleaf');

exports.get = function(req) {

  // Resolve the view
  var view = resolve('/site/view/my-first-page.html');

  // Define the model
  var model = {
    name: "John Doe"
  };

  // Render a thymeleaf template
  var body = thymeleaf.render(view, model);

  // Return the result
  return {
    body: body,
    contentType: 'text/html'
  };

};

Unlike controllers and descriptors, view files can reside anywhere in your project and have any valid file name. This allows for code reuse as multiple page components can share the same view. If the view file is in the same folder as the page controller then it can be resolved with only the file name resolve('file-name.html'). Otherwise, the full path should be used, starting with a '/' as in the example above.

Dynamic-content

We can send dynamic content to the view from the controller via the model parameter of the render function. We then need to use the rendering engine specific syntax to render it. The controller file above passed a variable called name and here is how to extract its value in the view using Thymeleaf syntax.

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <h1>My first page, with a view!</h1>
    <h2>Hello <span data-th-text="${name}">World</span></h2>
  </body>
</html>

More on how to use Thymeleaf can be found in the official Thymeleaf documentation

Regions

To be able to add components like images, component parts, or text to a page via Content Studio’s page editor, we first need to create at least one region. Regions can be declared in the page descriptor. Each region will be referenced by name.

Example regions in a [page].xml
<page>
  <display-name>My first page</display-name>
  <form />
  <regions>
    <region name="main"/>
  </regions>
</page>

You will also need to handle regions in the controller.

Example page controller using regions
var portal = require('/lib/xp/portal');

// Get the current content. It holds the context of the current execution
// session, including information about regions in the page.
var content = portal.getContent();

// Include info about the region of the current content in the parameters
// list for the rendering.
var mainRegion = content.page.regions["main"];

// Extend the model from previous example
var model = {
    name: "Michael",
    mainRegion: mainRegion
};

To make the Page Editor understand that an element is a region, it needs an attribute called data-portal-region with value being name of the region.

Example view using regions
<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <h1>My first page, with a view!</h1>
    <h2>Hello <span data-th-text="${name}">World</span></h2>
    <div data-portal-region="main">
      <div data-th-each="component : ${mainRegion.components}" data-th-remove="tag">
        <div data-portal-component="${component.path}" data-th-remove="tag" />
      </div>
    </div>
  </body>
</html>

We can now use the Page Editor drag and drop interface to drag components into our page.

Layout

Layouts - like page components a made up from a descriptor and a controller.

Descriptor

Like pages, layouts needs a descriptor. The file must be placed within the component structure as follows /resources/site/layout/[layout-name]/[layout-name].xml.

Similar to the page descriptor, the layout descriptor defines regions, where other components may be placed.

Example layout descriptor
<layout>
  <display-name>70/30</display-name>
  <form />
  <regions>
    <region name="left"/>
    <region name="right"/>
  </regions>
</layout>
In addition to the standard configuration options, Content Studio supports custom filter definitions that limit which content items a layout may be placed on.

Controller

The JavaScript file responsible for rendering the layout.

Like pages, the layout controller must be placed within the component structure as follows /resources/site/layouts/[layout-name]/[layout-name].js.

Example layout controller
var portal = require('/lib/xp/portal');
var thymeleaf = require('/lib/thymeleaf');

exports.get = function (req) {
    var component = portal.getComponent();

    return {
        body: thymeleaf.render(resolve('./layout-70-30.html'), {
            leftRegion: component.regions["left"],
            rightRegion: component.regions["right"],
        })
    };

};

View

Views are optional, but enable clean production of server-side markup for the component. The sample view below is created in Thymeleaf, but it could be created in any view engine that is supported.

Example layout defined in Thymeleaf
<div class="row">
  <div data-portal-region="left" class="col-sm-8">
    <div data-th-each="component : ${leftRegion.components}" data-th-remove="tag">
      <div data-portal-component="${component.path}" data-th-remove="tag" />
    </div>
  </div>

  <div data-portal-region="right" class="col-sm-4" >
    <div data-th-each="component : ${rightRegion.components}" data-th-remove="tag">
      <div data-portal-component="${component.path}" data-th-remove="tag" />
    </div>
  </div>
</div>
Markup generated for a layout must have a single root element.

Part

Part components are similar to layouts, but do not support regions - making them leaf-nodes in a component structure.

Descriptor

Like pages and layouts, parts needs a descriptor. The descriptor file must be placed within the component structure as follows /resources/site/parts/[part-name]/[part-name].xml.

Example part descriptor
<part>
  <display-name>Content List</display-name>
  <form />
</part>
In addition to the standard configuration options, Content Studio supports custom filter definitions that limit which content items the part may be placed on.

Controller

Just like pages and layouts, parts will need a controller in order to render properly.

The controller file must be placed within the component structure as follows /resources/site/parts/[part-name]/[part-name].js.

Markup generated for a part must have a single root element.

Custom icon

XP XP 7.2.0 7.2.0

It’s possible to define a custom icon for part which then will be used instead of the default one in the Page Component tree and dropdowns with part descriptors. Simply place an icon (in either PNG or SVG format) with the same name as the descriptor schema into the same folder. For example, if folder/part is called mypart then part descriptor is called mypart.xml and the icon file should be called either mypart.svg or mypart.png. In the screenshots below parts "HTML Area Example" and "Image filters test" are using custom icons.

Part descrtiptor list
Page Components tree

Text

The Text component allows content editors to place and format text into any region of a page.

Text components also support rich content such as Images and Macros

The formatting and macro options are the same as those for the HtmlArea inputs that can be found in content types and other configuration forms in the Content Studio.

Here is an example. The blue box is a text component being edited:

Component list

Fragment

Fragments are essentially components stored as individual content. When a fragment is altered, the change is instantly visible on every page that uses it. This also adds a range of new possibilities when creating pages, such as time based publishing and permission control.

Creating fragments

Fragments are essentially component instances that can be re-used across pages. A fragment can be created from any component on a given page.

A fragment is saved as a separate content instance, which means it can be managed and published like any other content item.

Component list

Image

Image comonent is deprecated and will be removed in XP 8.0. Use "Insert Image" feature of the Text Component instead.

The Image component allows content editors to place an image into any region on a page without writing any code.

Component list

Contents