Blog tech

Générer des graphiques C3js avec AngularJS

Rédigé par Numa Claudel | 30 avril 2015

Récemment, pour un développement de dashboards, j’ai cherché une librairie pour générer des graphiques de façon simple et sexy. Après quelques recherches, mon choix s’est porté sur C3js.

Comme le stipule le titre, C3js est une librairie de graphiques pré-construits basée sur D3js. Nous avons donc les types de graphiques les plus courants, avec les possibiltés de D3js, mais ce n’est pas tout, C3js nous offre aussi la possibilité de mettre à jour les données en live avec de beaux effets. Si vous allez sur la page d’accueil, essayez d’utiliser le bouton “Start Demo” tout en haut de la page, il donne tout de suite un bon aperçu des possibilités.

Utilisant AngularJS, il me fallait des directives pour générer et manipuler les graphiques. Après quelques recherches sur le net, et ne trouvant pas exactement toutes les fonctionnalités que je recherchais avec cette configuration, il ne restait plus qu’à en élaborer des nouvelles.

Aujourd’hui je vous propose donc d’explorer un peu les possibilités que nous offre C3js, ainsi que quelques exemples utilisant les directives résultantes de ce développement.

API C3js

Alors, nous avons des graphiques D3js de type “line”, “spline”, “step”, “area”, “bar”, “scatter plot”, “pie”, “donut” et “gauge” directement disponibles grâce à C3js.

Nous avons aussi des exemples de graphiques sur cette page, qui est assez fournie. Le code de chaque exemple est éditable en live, ce qui est très pratique pour faire des essais.

L’API se décompose en plusieurs sections:

  • Chart: options générales du graphique (id de l’élément, dimensions, bibliothèque de couleurs, évènements, etc …)
  • Data: les données servant à générer le graphique (source, type, libellé, valeur de l’abscisse, couleur, ordre, évènements, etc …)
  • Axis: options des axes (affichés/cachés, autre axe des ordonnées, valeurs, plage de valeurs, libellé, etc …)
  • Grid: affichage de grilles ou lignes optionnelles supplémentaires (affichés/cachés, valeurs et options des lignes supplémentaires, etc …)
  • Region: définission de régions sur le graphique (valeurs et options des régions)
  • Legend: options des légendes des courbes (affichés/cachés, positions, évènements, etc …)
  • Tooltip: options des infos bulles (affichés/cachés, regroupement, formatage, etc …)
  • Subchart: options pour l’affichage d’un mini graphique permettant de se déplacer sur l’axe des abscisses et n’afficher qu’une sous partie du graphique parent
  • Zoom: options permettant de zoomer/dézoomer avec un scroll de souris sur le graphique
  • Point: options au niveau des points générés par les données du graphique (affichés/cachés, taille, etc …)
  • Line, Area, Bar, Pie, Donut, Gauge: options spécifiques à certains types de courbes
  • API: fonctions permettant de manipuler et d’intéragir avec le graphique
  • Class: classes CSS pour ajuster le design

Je trouve cette documentation bien détaillée, et les multiples exemples aident beaucoup à la compréhension.

API des directives C3js

Pour suivre au maximum l’API proposée par C3js, j’ai fait le choix de créer une directive par section de l’API. La directive de base se nomme szC3Chart et comprend les parties “Chart” et “Data”.

Pour définir un graphique, il faut au minimum le déclarer comme suit:

<sz-c3-chart bindto="mon-graphique"></sz-c3-chart>

Pour lui passer des données, j’ai défini les types columns, rows ou json. Il faut donc formater les données correctement et les passer par le bon attribut à la directive. Disons que nous avons une variable columns dans le scope, avec le format columns attendu par C3js:

$scope.columns: [
  ['data1', 30, 20, 50, 40, 60, 50],
  ['data2', 200, 130, 90, 240, 130, 220],
  ['data3', 300, 200, 160, 400, 250, 250]
];

il est alors possible de la passer à la directive par l’attribut columns:

<sz-c3-chart bindto="chart-id"
             columns="columns">
</sz-c3-chart>

L’attribut type permet de définir le type global du graphique (on peut aussi définir un type par courbe, c’est un peu plus loin):

<sz-c3-chart bindto="chart-id"
             type="pie"
             columns="columns">
</sz-c3-chart>

Pour ajuster les dimensions du graphique, les attributs size et padding donnent accès aux options C3js du même nom:

<sz-c3-chart bindto="chart-id"
             size="{ width: 600 }"
             padding="{ bottom: 12 }"
             type="pie"
             columns="columns">
</sz-c3-chart>

Pour chacune des autres sections de l’API de C3js, une directive correspondante est disponible. Par exemple pour changer la position de la légende:

<sz-c3-chart bindto="chart-id"
             size="{ width: 600 }"
             padding="{ bottom: 12 }"
             type="pie"
             columns="columns">
  <legend position="right"></legend>
</sz-c3-chart>

Et ainsi de suite.

J’ai par contre redéfini au niveau de la directive szC3Chart, une partie de l’API proposée par C3js qui permet de donner les options de chaque courbe. Au lieu de passer les attributs names, types et colors séparément, vous pouvez définir un tableau contenant ces options:

$scope.curvesOptions = [
  { id: 'data1', type: 'bar', name: "Données numéro 1", color: '#D62728' },
  { id: 'data2', type: 'bar', name: "Données numéro 2", color: '#FF7F0E' },
  { id: 'data3', type: 'line', name: "Données numéro 3", color: '#1F77B4' }
];

et le passer à la directive par l’attribut curvesOptions:

<sz-c3-chart bindto="chart-id"
             size="{ width: 600 }"
             padding="{ bottom: 12 }"
             columns="columns"
             curves-options="curvesOptions">
  <legend position="right"></legend>
</sz-c3-chart>

Notez que dans le cas de données de type JSON, il faudra au minimum passer les id des courbes devant être tracées à curvesOptions.

Pour mettre à jour les données du graphique avec un bel effet de transition, il faut envoyer un évènement ReloadData avec les nouvelles données en paramètre, à partir d’un contrôleur englobant la directive:

$scope.$broadcast('ReloadData', {
  columns: [
    ['data1', 22, 23, 36, 33, 42, 55],
    ['data2', 133, 140, 122, 210, 60, 327],
    ['data3', 355, 210, 188, 304, 281, 213]
  ]
});

Il est aussi possible de mettre à jour dynamiquement l’axe des abscisses, en envoyant un évènement ChangeRange avec les nouvelles valeurs minimum et maximum que le graphique doit afficher.

Je n’ai pas implémenté toute l’API proposée par C3js, mais en consultant les parties relatives aux scopes de chaque directive, vous aurez leur API actuelle. Par exemple pour la directive de base:

scope: {
  // chart
  bindto: '@',
  size: '=',
  padding: '=',
  colorPattern: '=',

  // data
  x: '@',
  order: '@',
  labels: '@',
  type: '@',
  axes: '=',
  colors: '=',
  groups: '=',
  json: '=',
  rows: '=',
  columns: '=',
  curvesOptions: '=',
  color: '&'
}

Quelques graphiques

Voyons ce que donne le premier graphique de type “pie”, juste avant l’exemple avec les options:

.controller('PieController', ['$scope', function($scope) {

  $scope.columns: [
    ['data1', 30, 20, 50, 40, 60, 50],
    ['data2', 200, 130, 90, 240, 130, 220],
    ['data3', 300, 200, 160, 400, 250, 250]
  ];

}]);
<div ng-controller="PieController">
  <sz-c3-chart bindto="chart-id"
               type="pie"
               size="{ width: 600 }"
               padding="{ bottom: 12 }"
               columns="columns">
    <legend position="right"></legend>
  </sz-c3-chart>
</div>

Voyons maintenant celui avec les 2 courbes de type “bar” et une de type “line”, ainsi que la mise à jour des données:

See the Pen C3js bar and line chart with live update by Numa Claudel (@Claun) on CodePen.

Un troisième avec cette fois-ci une plage de temps sur l’axe des abscisses, avec mise à jour de la plage affichée:

See the Pen C3js line timeseries chart with axis move by Numa Claudel (@Claun) on CodePen.

L’attribut x sert à définir quelles données sont représentées par l’axe des abscisses, ici les dates. Dans le cas de données de type JSON, il faut passer à l’attribut curvesOptions une entrée de plus, contenant l’id de l’axe, comme si c’était une courbe, et la value (les données qu’il représente):

$scope.curvesOptions = [
  { id: 'x', value: 'x' }
];

Conclusion

Pour ma part je trouve que les graphiques proposés par C3js sont assez plaisants, qu’en pensez-vous ?

Pour ce qui est des directives que je vous propose, mon objectif était d’utiliser C3js au travers d’AngularJS, en conservant son API et en essayant d’avoir les meilleures performances possibles.

angular-c3js est disponible sur notre Github. N’ayant pas couvert toute l’API de C3js, sentez vous donc libre de forker le dépot pour adapter les directives à vos besoins.

L’équipe Synbioz.

Libres d’être ensemble.