Page components

Contents

Page components

Pages are composed from a set of editorially manageable 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/
          <component-name>/
            <component-name>.js
            <component-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/xp/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 <<https://www.thymeleaf.org/documentation.html#,the official Thymeleaf documentation>>

Regions

To be able to add components like images, component parts, or text to our page via the Page Editor drag and drop interface, we 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>
  <config />
  <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 are used in conjunction with regions to organize the structure of the various component parts that will be placed on the page via Page Editor drag and drop. Layouts can be dropped into the page regions and then parts can be dragged into the layout. This allows multiple layouts (two-column, three-column, etc.) on the same page and web editors can change things around without touching any code. Making a layout is similar to making pages and part components. Layouts cannot be nested.

Layout contains - like pages and parts - a descriptor, a controller and a view, and should be placed in the folder site/layouts/[layout-name]

Descriptor

The layout descriptor defines regions within the layout where parts can be placed with the Page Editor. The file must be named [layout-name].xml.

Example layout descriptor
<layout>
  <display-name>70/30</display-name>
  <config/>
  <regions>
    <region name="left"/>
    <region name="right"/>
  </regions>
</layout>

View

A layout view defines the markup for the layout 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>
The HTML generated for the layout view must have a single root element.

Styling

For a layout to have any meaning, some styling must be applied to the view. The desired CSS should be placed in the /assets folder of the application, and included in the page where the layout should be supported. For example, the view my-first-page.html supports Bootstrap layouts:

Example page descriptor
<head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <link data-th-href="${portal.assetUrl({'_path=css/bootstrap.min.css'})}" href="../assets/css/bootstrap.min.css" rel="stylesheet"/>
</head>

Part

Part components are reusable, configurable components that can be placed into any region of a page with the page editor Context Panel. This allows content editors to build and customize pages without writing any code. There are no built-in part components. Each one is custom made in the application code. Parts are typically created to render custom content, lists of content, forms, etc.

The first step in adding a part component to a page is to edit the page content and open the component widget’s “Insert” tab. Drag the part component placeholder (puzzle piece) to the desired location on the page. The part placeholder will now appear as a blue box with a dropdown selector. The same part dropdown selector will appear in the inspect tab. Use one of the selectors to find the desired part component. Once a part component is selected, the placeholder will be replaced with the actual part and the Inspect tab will show the part’s configuration options in a form.

Some parts won’t have any configuration. Parts with configuration options are independently configured. This means that the same part component can be added to multiple pages, or even multiple times in the same page, and each instance can have different configuration values.

Component list

Text

The Text component allows content editors to place and format text into any region on a page without writing any code. Images can also be added inside text components. Macros allow Twitter tweets, YouTube videos, embedded code, and no-format text to be added as well. 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. The only difference is that the formatting toolbar is at the top of the page for text components.

In the <<https://developer.enonic.com/docs/content-studio/master/editor/page-editor#,Page Editor>>, drag a Text component from the insert tab to the desired region on the page. A cursor will appear inside the text component and editing can begin. If another component is selected, the text component will need to be double-clicked to resume editing.

Here is an example. The blue box is a text component being edited. Although the pointer does not show in this screenshot, it is hovering above the Underline box in the formatting toolbar, in order to underline the highlighted text, "Blog site!" :

Component list

Image

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

In the Page Editor, drag an Image component placeholder from the insert tab to the desired region on the page. Once placed, the empty image placeholder contains an image selector that can be used to find and select any previously uploaded image content. If the name of the image is known, start typing it in the box to filter the list of images. If the name is not known, use the down arrow to open a list of image thumbnails to choose from. Note that the list will contain all image content items in the XP installation, including images that were created in a different site of a multi-site environment.

If the desired image does not already exist as a content, upload it with the button on the right side of the image selector dropdown. The new image content will be created as a child of the page being edited, but it could be moved later if needed.

The inspect tab will also show the dropdown image selector. If the image needs to be changed at a later time, this is the place to do it.

Component list

Fragment

Fragments are created as content from an instance of another component. What makes a fragment special is that it uses the same configuration on every page where it’s added. When a fragment content is altered, the change is instantly visible on every page that uses it. All other components are independently configured.

Creating fragments

Fragments can be created from any component on a page. When a fragment is created, it makes a content copy of the part, layout, image or text component. In the page editor, right-click the desired component and select “Create fragment” from the context menu. The new fragment content is created as a child of the page being edited. The fragment content will open in a new editor tab where its name and configuration can be changed. At the same time, the component that was copied is replaced with the new fragment.

Using fragments

Once a fragment content has been created, it can be added to pages with the page editor. Drag a fragment placeholder from the “Insert” tab of the Context Panel to the desired location on the page. Use the dropdown selector in the placeholder to find the desired fragment content. Once selected, the fragment will appear.

Component list

Contents