Skip to content

Custom blocks and extensions

EntryScape blocks allows the definition of new blocks in addition to the blocks listed in the reference section. Let's list a few reasons for why you would like to define your own custom block.

  1. Providing many parameters inside html can sometimes be a bit clunky and error-prone. Especially for parameters that require templates.
  2. You want to use a specific setup of blocks in many places and see the benefit of maintaining the configuration in a single place.
  3. You want your solution to be maintained separately for purposes of documentation, collaboration, version history etc.
  4. Your use of blocks is complicated with nested blocks.

Defining custom blocks

As described in the section on global level parameters it is possible to provide parameters in a special global variable __entryscape_config. That global variable needs to be defined before the main script is loaded, hence the webpage could look like the following:

<span data-entryscape="myblock"></span>
<script src="myblocks.js"></script>
<script src="https://static.cdn.entryscape.com/blocks/1/app.js"></script>

Where the file myblock.js contains the global level parameters, including the custom blocks in the blocks parameter:

window.__entryscape_config = {
  entrystore: "http://example.com/store",
  blocks: [
    {
      block: 'myblock',
      extends: 'template',
      template: '<h1>{{text}}</h1>'
    },
  ]
}

Collecting custom blocks into an extension

If you want to define a set of custom blocks that are reusable and possible to combine with other sets of custom blocks it is recommended to write the javascript in the following manner to avoid overwriting previously defined custom blocks:

var ecnf = '__entryscape_config';window[ecnf] = window[ecnf || {};
window[econf].blocks = (window[ecnf] || []).concat([
  {
    block: 'myblock',
    extends: 'template',
    template: '<h1>{{text}}</h1>'
  }
]);

Extending in several steps

You can extend both the built-in blocks and other custom blocks. However, custom blocks are defined in order and the block you are extending have to be defined already for this to work. That is, you cannot extend a block that will be defined further down i the blocks array or in an extension being loaded later.

Here is a realistic example of a searchList being extended in two steps. The first custom block (datasetSearch) provides a searchable list of datasets where each row provides a link to a separate page for that dataset. The second custom block (datasetSearchExpandable) provides the same search functionality but no links. Instead, it makes each dataset (row) expandable by providing additional parameters and overriding the rowhead template.

blocks: [{
  block: 'datasetSearch',
  extends: 'searchList',
  rdftype: 'dcat:Dataset',
  limit: '10',
  initsearch: true,
  listplaceholder: '<h4>No matches</h4>',
  rowhead: '{{link click="details.html"}}'
},     
{
  block: 'datasetSearchExpandable',
  extends: 'datasetSearch',
  expandTooltip: 'Show details of dataset',
  unexpandTooltip: 'Hide details of dataset',
  rowhead: '<h1>{{text}}</h1><p>{{text property="dcterms:description"}}</p>',
  rdformsid: 'dcat:onlyDataset'
  clickExpand: true
}]

Priority of parameters

Just like the example above showed you can extend custom blocks in several steps and override parameters. Furthermore, you can override the parameters when using the block in html. For example, to continue on the example above we override the limit parameter like this:

<div data-entryscape="datasetSearch" data-entryscape-limit="20"></div>

Introducing new block level parameters

To avoid creating a wide range of very similar blocks you can introduce new parameters to provide flexibility. Let's take the example with the dataset search above and introduce a parameter that controls if each dataset title should be rendered as a link or as a heading:

{
  block: 'datasetSearchFlexible',
  extends: 'datasetSearch',
  linkout: true,
  rowhead: '{{#if linkout}}{{link click="details.html"}}{{/if}}' +
  '{{#unless linkout}}<h1>{{text}}</h1>{{/unless}}'
},

In some situations you want to lift out a parameter usage on an inner block, this can be achieved by the special inherit syntax of parameters. Let's make a version of the expandable list where it is possible to control the form and which fields that should be shown when expanded:

{
  block: 'perferctDatasetSearch',
  extends: 'datasetSearchExpandable',
  filterPredicates: 'dcterms:title,dcterms:description'
  rowexpand: '{{view template="inherit:rdformsid"' +
  'filterPredicates="inherit:filterPredicates"}}
}

Using global level state parameters

If a parameter will be used by several blocks it can be introduced as a global level state parameters. To use the parameter inside a block simply write state.followed by the parameter name. Like this:

state: {
    heart: {
        type: 'string',
        value: '❤️'
    }
},
blocks: [
    {
      block: 'datasetSearchFlexible',
      extends: 'datasetSearch',
      linkout: true,
      rowhead: '{{state.heart}}<h1>{{text}}</h1>'
    }
]

The state parameters can be dynamic but if you want the block to react to change you have to allow it by subscribing to the changes. This is done by adding the subscribe property and specifying which parameters to subscribe to. The parameters can be given as an array or as a comma separated string

state: {
    showTitle: {
        type: 'boolean',
        dynamic: true,
        value: 'true'
    },
    showDescription: {
        type: 'boolean',
        dynamic: true,
        value: 'false'
    }
},
blocks: [
    {
        block: 'datasetSearchFlexible',
        extends: 'datasetSearch',
        subscribe: ['showTitle'],
        rowhead: '{{#if state.showTitle}}<h1>{{text}}</h1>{{/if}}'
    },
    {
        block: 'ingress',
        extends: 'template',
        subscribe: 'showTitle,showDescription',
        template: '{{#if state.showTitle}}<h1>{{text}}</h1>{{/if}}' +
            '{{if state.showDescription}}<p>Beskrivande text</p>{{/if}}'
    }
]

Browser compatability considerations

Note that unless you build the extension files using some mechanism you should be very careful when using modern javascript constructs to maximize compatability with older browsers. For instance, writing the handlebars using multiline template literals can be very tempting but will break support for browsers like Internet Explorer 11.

Good examples

The open data extensions can be a good starting point to get some insight into the capabilities of custom blocks:

  1. English open data extension
  2. Italian open data extension
  3. Swedish open data extension
  4. German open data extension