Adapters

Adapters is a way to modify just about anything in amCharts 4 using custom functions.

Virtually anything in amCharts 4 can be modified using custom functions.

For example, your custom function can modify axis labels before they are outputted. You can have an adapter to modify language prompts, or override default settings, or modify object's property return different value.

Adapter's make everything supercustomizable.

Adding adapters

Virtually any object in amCharts 4 has a property adapter. Use its add() method to add your adapter (custom function) for the specific purpose.

To make it clear how it works, let's start with an example, which wraps contents of the axis tooltip into ">>>" and "<<<":

dateAxis.adapter.add("getTooltipText", function(text, target, key) {
 return ">>> " + text + " <<<";
});
dateAxis.adapter.add("getTooltipText", function(text, target, key) {
 return ">>> " + text + " <<<";
});
{
  // ...
  "xAxes": [{
    "type": "DateAxis",
    // ...
    "adapter": {
      "getTooltipText": function(text, target, key) {
        return ">>> " + text + " <<<";
      }
    }
  }]
}

The first parameter to add() is the adapter name. In this case we know adapter name for modifying axis tooltip text is "getTooltipText". (see next section for explanation on how to find available adapter names)

The second parameter is our custom function.

How adapters work

Here's how it goes in adapter world.

Say we have a chart with Date axis. We also have a Cursor on our chart.

In this combination, whenever we hover over plot area, a Cursor will draw crosshair lines to all axes. In each axis a tooltip will be shown with corresponding contents - in our case of Date axis - a date which would correspond to position of the cursor.

Now, when tooltip is about to be shown, Date axis generates a content to be shown in the tooltip.

Then, it checks if there are any adapters added for "getTooltipText" on that Date axis. If there are (such as in our case), it calls our custom function passing current tooltip content into it as a parameter.

It then takes the return value of the custom function and uses as tooltip content.

IMPORTANT A customer adapter function needs to return value of the same type as input.

Custom function can also define a second parameter. If it does, it will hold an adapter target - an object the adapter is set for.

Here's a real chart showcasing the above: (go ahead and hover over plot area)

See the Pen amCharts V4: Using adapters by amCharts (@amcharts) on CodePen.24419

Adapter parameters

The two parameters to add() function we saw earlier are required. There are more, though.

Here's the full list.

# Parameter Comment
1 adapter A unique identifier, e.g. "opacity"
2 callback A function which will be called that can modify the value.
3 priority Priority of the adapter. The higher priority, the later in the adapter chain the function will be called, thus most chance of having "the last word".
4 scope A scope the function will be called in.

Chaining multiple adapters

As we mentioned earlier, each adapter can contain multiple callbacks.

When it's time to apply the the adapter to a value, the object will apply first adapter in line, take its output value, pass it in into the next adapter, and so on.

The order in which adapters are applied, is determined by adapter's priority value. The higher the value, the later in line adapter will be applied.

If two adapters share the same priority (which is optional, by the way), they will be applied in the same order they were added in.

This allows combining the "efforts" of multiple functions in dynamically updating the values.

Let's build an example, which has two adapters, attached to a Value axis labels. The first one will add some prefix text to the value, while the other will check if there value is negative, and will apply red color formatting, if it is.

// This adapter makes negative labels red
valueAxis.renderer.labels.template.adapter.add("text", (label, target, key) => {
  if (target.dataItem && (target.dataItem.value < 0)) {
    return "[red]" + label;
  } else {
    return label;
  }
});

// This adapter adds custom text to the label
valueAxis.renderer.labels.template.adapter.add("text", (label, target, key) => {
  return label + " units";
});
// This adapter makes negative labels red
valueAxis.renderer.labels.template.adapter.add("text", (label, target, key) => {
  if (target.dataItem && (target.dataItem.value < 0)) {
    return "[red]" + label;
  } else {
    return label;
  }
});

// This adapter adds custom text to the label
valueAxis.renderer.labels.template.adapter.add("text", (label, target, key) => {
  return label + " units";
});
{
  // ...
  "yAxes": [{
    "type": "ValueAxis",
    // ...
    "renderer": {
      "labels": {
        "adapter": [{
          "key": "text",
          "callback": function(label, target, key) {
            if (target.dataItem && (target.dataItem.value < 0)) {
              return "[red]" + label;
            }
            else {
              return label;
            }
          }
        }, {
          "key": "text",
          "callback": function(label, target, key) {
            return label + " units";
          }
        }]
      }
    }
  }]
}

Let's see how this works out beautifully on a live chart:

See the Pen amCharts V4: Using adapters (2) by amCharts (@amcharts) on CodePen.24419

Finding available adapters

Each class in our Class reference has a section called Adapters. Refer to it to find the list of adapters supported by that objects of that class.

For example, here's a list of Value axis adapters. Each listed adapter shows the type of input it will receive and what it must return.

Sample adapter reference

Removing adapters

In case you don't need an adapter anymore, you can use remove() method to, well, remove it.

The method accepts two parameters: adapter identifier (the one you used to add adapter for) and priority (if you specified it).

dateAxis.adapter.remove("getTooltipText");
dateAxis.adapter.remove("getTooltipText");

Global adapters

So far, we've looked at the adapters that can be attached to a specific object. Let's call them local adapters.

But what if we want to build a plugin that automatically affects properties of objects of the specific type, without needing to explicitly call add() on each and every instance?

For that we have "global" adapters.

Global adapters are attached to a readily available object am4core.globalAdapter, and work similarly to local adapters, except we also specify the type of object we want to add adapter to.

Instead of add() we also need to use addAll().

Let's see how this looks code-wise:

am4core.globalAdapter.addAll<am4charts.IAxisLabelAdapters, am4charts.AxisLabel, "text">(am4charts.AxisLabel, "text", (label, target) => {
  if (!target.dataItem) {
    return label;
  }
  let axis = target.dataItem.component;
  if ((axis instanceof am4charts.ValueAxis) && !(axis instanceof am4charts.DateAxis) ) {
    if (target.dataItem.value < 0) {
      return "[red]" + label + " units";
    } else {
      return label + " units";
    }
  }
  else {
    return label;
  }
});
am4core.globalAdapter.addAll(am4charts.AxisLabel, "text", function(label, target) {
  if (!target.dataItem) {
    return label;
  }
  var axis = target.dataItem.component;
  if ((axis instanceof am4charts.ValueAxis) && !(axis instanceof am4charts.DateAxis) ) {
    if (target.dataItem.value < 0) {
      return "[red]" + label + " units";
    } else {
      return label + " units";
    }
  }
  else {
    return label;
  }
});

Here's the same result as before, using global adapter, which means it will be applied to every axis, on every chart on the page:

See the Pen amCharts V4: Using adapters (3) by amCharts (@amcharts) on CodePen.24419

Common pitfalls

Referencing data item

In some cases, the logic in an adapter might depend on the actual data. In such cases you might want to refer to a dataItem of the adapter target.

Important thing to know here is that you always need to check if data item is available.

In some cases or situations dataItem might not be set, which will result in an error.

valueAxis.renderer.labels.template.adapter.add("text", (label, target, key) => {
  if (target.dataItem && (target.dataItem.value < 0)) {
    return "[red]" + label;
  } else {
    return label;
  }
});
valueAxis.renderer.labels.template.adapter.add("text", (label, target, key) => {
  if (target.dataItem && (target.dataItem.value < 0)) {
    return "[red]" + label;
  } else {
    return label;
  }
});
{
  // ...
  "yAxes": [{
    "type": "ValueAxis",
    // ...
    "renderer": {
      "labels": {
        "adapter": [{
          "key": "text",
          "callback": function(label, target, key) {
            if (target.dataItem && (target.dataItem.value < 0)) {
              return "[red]" + label;
            }
            else {
              return label;
            }
          }
        }]
      }
    }
  }]
}

Circular references

Currently, amCharts 4 adapters do not have built-in protection against circular usage.

That means if you try to access the same property the adapter is for in the adapter's callback function, it will trigger adapter function again, which in turn will trigger itself again, and so on resulting in a dead loop and unresponsive browser.

Even if you try to console.log(target) it will result in a dead loop, since browser will try to access all properties of the target object, eventually hitting the same property.

Related content

Check out the Adapter-related tutorials for more real-life examples.

Related demos