Angular Chart - Bi-Directional Bar Chart

In a bi-directional bar chart, two sets of bars "grow" from the axis in the center in two opposite directions. This chart can be used to compare two values side by side. This article describes the main steps to implement a bi-directional bar chart using the Chart UI component.

View Demo

Prepare Data

To prepare data for the bi-directional bar chart, convert one of the two sets of bars' values from positive to negative. You can change the sign in each value manually or use the DevExtreme DataSource's map function:

jQuery
JavaScript
var population = [
    { age: "0-4", male: 3.1, female: 2.9 },
    { age: "5-9", male: 3.1, female: 3.0 },
    // ...
];

$(function() {
    $("#chartContainer").dxChart({
        dataSource: {
            store: {
                type: "array",
                data: population
            },
            map: function (dataItem) {
                return {
                    age: dataItem.age,
                    male: dataItem.male,
                    female: -dataItem.female // Changing the values' sign
                }
            }
        }
    });
});
Angular
TypeScript
HTML
import { DxChartModule } from "devextreme-angular";
import DataSource from "devextreme/data/data_source";
import ArrayStore from "devextreme/data/array_store";
// ...
export class AppComponent {
    dataSource: DataSource;
    population = [
        { age: "0-4", male: 3.1, female: 2.9 },
        { age: "5-9", male: 3.1, female: 3.0 },
        // ...
    ];
    constructor() {
        this.dataSource = new DataSource({
            store: new ArrayStore({
                data: this.population
            }),
            map: (dataItem) => {
                return {
                    age: dataItem.age,
                    male: dataItem.male,
                    female: -dataItem.female // Changing the values' sign
                }
            }
        });
    }
}
@NgModule({
    imports: [
        // ...
        DxChartModule
    ],
    // ...
})
<dx-chart
    [dataSource]="dataSource">
</dx-chart>
Vue
App.vue
<template> 
    <DxChart ...
        :data-source="dataSource">
    </DxChart>
</template>

<script>
import DxChart from 'devextreme-vue/chart';
import DataSource from 'devextreme/data/data_source';
import ArrayStore from 'devextreme/data/array_store';

const population = [
    { age: '0-4', male: 3.1, female: 2.9 },
    { age: '5-9', male: 3.1, female: 3.0 },
    // ...
];

export default {
    components: {
        DxChart
    },
    data() {
        return {
            dataSource: new DataSource({
                store: new ArrayStore({
                    data: population
                }),
                map: (dataItem) => {
                    return {
                        age: dataItem.age,
                        male: dataItem.male,
                        female: -dataItem.female // Changing the values' sign
                    }
                }
            })
        };
    }
}
</script>
React
App.js
import React from 'react';
import Chart from 'devextreme-react/chart';
import DataSource from 'devextreme/data/data_source';
import ArrayStore from 'devextreme/data/array_store';

const population = [
    { age: '0-4', male: 3.1, female: 2.9 },
    { age: '5-9', male: 3.1, female: 3.0 },
    // ...
];

class App extends React.Component {
    constructor(props) {
        super(props);

        this.dataSource = new DataSource({
            store: new ArrayStore({
                data: population
            }),
            map: (dataItem) => {
                return {
                    age: dataItem.age,
                    male: dataItem.male,
                    female: -dataItem.female // Changing the values' sign
                }
            }
        });
    }

    render() {
        return (
            <Chart ...
                dataSource={this.dataSource}>
            </Chart>
        );
    }
}

Configure the Series

You need two Stacked Bar series for the bi-directional bar chart. The following code declares and binds them to the data source from the previous topic:

jQuery
JavaScript
$(function() {
    $("#chartContainer").dxChart({
        // ...
        commonSeriesSettings: {
            type: "stackedbar",
            argumentField: "age"
        },
        series: [{
            valueField: "male",
            name: "Male"
        }, {
            valueField: "female",
            name: "Female"
        }]
    });
});
Angular
HTML
<dx-chart ... >
    <dxo-common-series-settings
        type="stackedbar"
        argumentField="age">
    </dxo-common-series-settings>
    <dxi-series valueField="male" name="Male"></dxi-series>
    <dxi-series valueField="female" name="Female"></dxi-series>
</dx-chart>
Vue
App.vue
<template> 
    <DxChart ... >
        <DxCommonSeriesSettings
            type="stackedbar"
            argument-field="age"
        />
        <DxSeries 
            value-field="male"
            name="Male"
        />
        <DxSeries
            value-field="female"
            name="Female"
        />
    </DxChart>
</template>

<script>
import DxChart, {
    DxCommonSeriesSettings,
    DxSeries
} from 'devextreme-vue/chart';

export default {
    components: {
        DxChart,
        DxCommonSeriesSettings,
        DxSeries
    }
}
</script>
React
App.js
import React from 'react';
import Chart, {
    CommonSeriesSettings,
    Series
} from 'devextreme-react/chart';

class App extends React.Component {
    render() {
        return (
            <Chart ... >
                <CommonSeriesSettings
                    type="stackedbar"
                    argumentField="age"
                />
                <Series 
                    valueField="male"
                    name="Male"
                />
                <Series
                    valueField="female"
                    name="Female"
                />
            </Chart>
        );
    }
}

export default App;
See Also

Rotate the Chart

Bars in the Stacked Bar series are vertical by default. Change them to horizontal by setting the rotated property to true.

jQuery
JavaScript
$(function() {
    $("#chartContainer").dxChart({
        // ...
        rotated: true
    });
});
Angular
HTML
<dx-chart ...
    [rotated]="true">
</dx-chart>
Vue
App.vue
<template> 
    <DxChart ...
        :rotated="true">
    </DxChart>
</template>

<script>
import DxChart from 'devextreme-vue/chart';

export default {
    components: {
        DxChart
    }
}
</script>
React
App.js
import React from 'react';
import Chart from 'devextreme-react/chart';

class App extends React.Component {
    render() {
        return (
            <Chart ...
                rotated={true}>
            </Chart>
        );
    }
}
See Also

Customize the Appearance

UI elements like tooltips and axis labels display incorrect data when you convert the chart's values from positive to negative. You can fix this by customizing these elements' text:

jQuery
JavaScript
$(function() {
    $("#chartContainer").dxChart({
        // ...
        tooltip: {
            enabled: true,
            customizeTooltip: function (pointInfo) {
                return {
                    text: Math.abs(pointInfo.originalValue)
                };
            }
        },
        valueAxis: {
            label: {
                customizeText: function (axisValue) {
                    return Math.abs(axisValue.value) + '%';
                }
            }
        }
    });
});
Angular
HTML
TypeScript
<dx-chart ... >
    <dxo-tooltip
        [enabled]="true"
        [customizeTooltip]="customizeTooltip">
    </dxo-tooltip>
    <dxi-value-axis>
        <dxo-label [customizeText]="customizeLabel"></dxo-label>
    </dxi-value-axis>
</dx-chart>
import { DxChartModule } from "devextreme-angular";
// ...
export class AppComponent {
    // ...
    customizeTooltip (pointInfo) {
        return {
            text: Math.abs(pointInfo.originalValue)
        };
    }
    customizeLabel (axisValue) {
        return Math.abs(axisValue.value) + '%';
    }
}
@NgModule({
    imports: [
        // ...
        DxChartModule
    ],
    // ...
})
Vue
App.vue
<template> 
    <DxChart ... >
        <DxTooltip
            :enabled="true"
            :customize-tooltip="customizeTooltip"
        />
        <DxValueAxis>
            <DxLabel :customize-text="customizeLabel"/>
        </DxValueAxis>
    </DxChart>
</template>

<script>
import DxChart, {
    DxTooltip,
    DxValueAxis,
    DxLabel
} from 'devextreme-vue/chart';

export default {
    components: {
        DxChart,
        DxTooltip,
        DxValueAxis,
        DxLabel
    },
    methods: {
        customizeTooltip(pointInfo) {
            return {
                text: Math.abs(pointInfo.originalValue)
            };
        },
        customizeLabel(axisValue) {
            return `${Math.abs(axisValue.value)}%`;
        }
    }
}
</script>
React
App.js
import React from 'react';
import Chart, {
    Tooltip,
    ValueAxis,
    Label
} from 'devextreme-react/chart';

class App extends React.Component {
    render() {
        return (
            <Chart ... >
                <Tooltip
                    enabled={true}
                    customizeTooltip={customizeTooltip}
                />
                <ValueAxis>
                    <Label customizeText={customizeLabel} />
                </ValueAxis>
            </Chart>
        );
    }
}

function customizeTooltip(pointInfo) {
    return {
        text: Math.abs(pointInfo.originalValue)
    };
}

function customizeLabel(axisValue) {
    return `${Math.abs(axisValue.value)}%`;
}

You can also adjust the bar's width. See Specify the Bar Width for details.

This article outlined the steps to implement a bi-directional bar chart and provided code examples for each step. Refer to the Bi-Directional Bar Chart demo for the full code.

View Demo