List Templates

List templates is a unique concept introduced in amCharts 4. It's an Array-like collection of items, with a single object serving as a "template" when creating objects in the list.

List templates are used very widely in amCharts 4. This article will address basic concept and finer details of this feature.

Intended usage

We use list templates whenever we need multiple instances of similarly-configured objects of the same type, like labels, grid lines of the axis, etc.

This ensures that we can configure list's "template" once, and have the changes automatically applied to all new objects created in the list.

Many of the properties, in amCharts 4's classes, that require this piece of functionality, are already list templates. These properties are indicated with type ListTemplate in Class reference.

Some of these lists will be populated by you, while configuring the chart, like inserting Axis break instances.

While some of lists will filled out with items, created by the chart itself, i.e. Column series will automatically create items in its columns list. One for each data point in data.

List template examples

We already touched some of the examples in the previous section of this article. Let's take a closer look at some of the most common uses for list templates.

Self-populating lists

As we already mentioned Column series, let's start this with it.

Whenever we create a Column series on a, say, XYChart, we expect it to draw an exactly one column for each data point in our data.

Not wanting to disappoint us, Column series does exactly that. It automatically creates Column instance for every data point, and places them into its property columns, which, yes you guessed it right, is a list template.

We can use columns.template to set properties for all of those automatically-created columns, as we will learn later in this article.

Yet another example of self-populating list, are axis labels. Each axis has labels property, which is a template list, and you can use its template to configure how labels on the axis will look like.

Manual lists

Taking series example further, while labels was self-populating list, axis breaks is not. You add items to it manually, specifying where and how the line should break. If you have multiple breaks, you might want to configure breaks.template once, then just create breaks specifying start and end values, rather than all of the other matching settings for every single breaks. (read more about axis breaks)

We use this list to manually create objects in it. Whatever Sprite-like objects we create from its template or push directly to it, will be used to create bullets along each column.

Another manually-populated list template is chart's titles. Every chart has titles list which holds objects of type Label. We can use its template to configure appearance for all labels, then create instances directly in the list, only changing text part.

Usage

Configuring template

The epicenter of each list template is its template property, which holds a full fledged object instance of the type the list is for.

The object in template is not used directly. It serves as a "model" object. Whenever a new object is created in the list, all properties, and other features, like event listeners, adapters, and everything else, is copied to the new object from the "template" object.

So, whatever options (or events, or adapters, etc.) we need in every single object created in the list, we set them on the template.

Let's pick up on our Column series example.

Remember how we described that Column series has a template list property columns, which holds Column objects.

That means that we can set any property available to Column, to columns.template object.

The following example will show how to set fill color (fill property) for our columns, as well as attach "hit" event to all of them, to execute a custom function whenever column is click or tapped.

var series = chart.series.push(new am4charts.ColumnSeries());
series.columns.template.column.fill = am4charts.color("#ff0000");
series.columns.template.events.on("hit", function(ev) {
  console.log("clicked on:", ev);
});
var series = chart.series.push(new am4charts.ColumnSeries());
series.columns.template.column.fill = am4charts.color("#ff0000");
series.columns.template.events.on("hit", function(ev) {
  console.log("clicked on:", ev);
});
{
  // ...
  "series": [{
    "type": "ColumnSeries",
    // ...
    "columns": {
      "template": {
        "column": {
          "fill": "#ff0000"
        },
        "events": {
          "hit": function(ev) {
            console.log("clicked on:", ev);
          }
        }
      }
    }
  }]
}

You may wonder why we're not setting fill directly on template. This is because a Column (which is the type of objects in columns list and list's template), is a container and can consist of many additional elements, besides the square. The latter is represented by column property, hence us setting fill on it.

Now, whenever chart creates a column in our series, it will be filled with red color and will generate "clicked on..." message in browser console whenever you click or tap on any of the columns.

MORE INFO Check out our dedicated articles on "Series" and "Anatomy of an XY Chart" for more juice on series configuration options.

IMPORTANT If using JSON-based config, you do not need to use template tag. Click here for more info.

Manually creating objects

There are two ways to manually add objects to the list template:

  • push() them into list like it was a regular Array. In this case the object will be added as is, without applying any properties from the list's template object.
  • Create them directly in the list, using list's create() method.

Depending on the particular list, one of the methods might make more sense than the other.

Using push()

For example, if we have two distinctive series to add to the chart's series list, it might make sense to just create object instances and push() them into the list. For example:

let lineSeries = chart.series.push(new am4charts.LineSeries());
let columnSeries = chart.series.push(new am4charts.ColumnSeries());
// ... configure series
var lineSeries = chart.series.push(new am4charts.LineSeries());
var columnSeries = chart.series.push(new am4charts.ColumnSeries());
// ... configure series
{
  // ...
  "series": [{
    "type": "LineSeries",
    // ...
  }, {
    "type": "ColumnSeries",
    // ...
  }]
}

In the above example we have created new instances of different series, then go one configuring each on independently.

NOTE push() always returns the instance that was added to the list, so we can use its return value to store it into a variable for further use.

Using create()

Now, consider if we had a chart with 10, very similarly configured Line series. It would make sense to configure the list's template only once, then use create() to "stamp" new pre-configured instances of series and change only different properties.

An example for the above:

// Replacing current template with instance of LineSeries
chart.series.template = new am4charts.LineSeries()
chart.series.template.strokeWidth = 3;
chart.series.template.strokeOpacity = 0.7;

// Create actual series
let series1 = chart.series.create();
series1.dataFields.value = "visits1";
series1.name = "Measurement #1";

let series2 = chart.series.create();
series2.dataFields.value = "visits2";
series2.name = "Measurement #2";

let series3 = chart.series.create();
series3.dataFields.value = "visits3";
series3.name = "Measurement #3";

// ... and so on
// Replacing current template with instance of LineSeries
chart.series.template = new am4charts.LineSeries()
chart.series.template.strokeWidth = 3;
chart.series.template.strokeOpacity = 0.7;

// Create actual series
var series1 = chart.series.create();
series1.dataFields.value = "visits1";
series1.name = "Measurement #1";

var series2 = chart.series.create();
series2.dataFields.value = "visits2";
series2.name = "Measurement #2";

var series3 = chart.series.create();
series3.dataFields.value = "visits3";
series3.name = "Measurement #3";

// ... and so on

NOTE create() automatically creates an instance of the object and pushes it to the list, so there's not need to push() it again. It will also return the newly created instance, so you can store it into variable for later use and configuration.

Note about JSON

Currently, JSON-based config does not support having both template configured and individual items in the same list, hence the above example missing the JSON tab.

We're working on a way to fix this.

Propagating template changes

So far we have been dealing for pre-setting config for new list items. Once the object is created, and initial settings are propagated to it from template object, it's "out in the wild", without little connection to the templates.

If we change anything any properties of the template the changes will not be propagated to objects that are already instantiated.

That can be changed using template's applyOnClones property.

If you know you'll want to change settings on your template after list items are created, you can set it to template.applyOnClones = true to make all clones automatically take in any new changes on the template object they were cloned from.

Just to illustrate the point, we whipped together a demo that creates a Column series, then, with a press of a button, changes fill color on template, so you can see how applyOnClones makes that change propagate to the already existing columns.

See the Pen amCharts V4: List template by amCharts (@amcharts) on CodePen.

Object & list relationship

Sometimes, in your API forays, i.e. in a handler for event, you might end up with an object that you don't know where it came from.

To get yourself situated, you might want to check object's clonedFrom property, which, in case said object was created from a template in a list, will contain reference to the template object.

Or, in reverse, if you'd like to find the list of objects cloned from the template, you can look into its clones property.