
Un graphique à colonnes comportant des régions et des mois sur un même axe pose deux problèmes, et ce ne sont pas les mêmes problèmes. Le premier est que les étiquettes de catégorie se réduisent à une seule ligne — « Nord », « Janv. », « Nord », « Févr. » — et le lecteur doit regrouper mentalement quel mois appartient à quelle région. Le second est que, lorsqu'une série de taux de croissance est ajoutée à côté d'une série de ventes qui se chiffre en millions, le taux de croissance devient une ligne plate collée à la ligne de base, car un seul axe des valeurs ne peut pas servir deux ordres de grandeur à la fois.
Les étiquettes de catégorie multi-niveaux corrigent le premier. Un axe secondaire corrige le second. Ce sont des fonctionnalités indépendantes qui se trouvent être utiles sur le même graphique, et Spire.XLS for JavaScript gère les deux via l'API d'axe du graphique — directement dans le navigateur sous WebAssembly, les fichiers transitant par un système de fichiers virtuel (VFS) et sans aucun backend.
Pour la configuration du projet, consultez Intégrer Spire.XLS for JavaScript dans un projet React. Les exemples ci-dessous supposent que le package est installé et que le module WebAssembly a été initialisé.
Quand un seul axe ne suffit pas
Les deux problèmes apparaissent sur le même type de feuille de calcul — une feuille où les catégories présentent une hiérarchie et où les valeurs s'étalent largement — mais ils proviennent d'endroits différents :
| Problème | Son origine | À quoi ressemble le graphique | Ce qui le corrige |
|---|---|---|---|
| Les étiquettes s'empilent sur une seule ligne | Les catégories sont hiérarchiques (région → mois, année → trimestre) mais l'axe les traite comme plates | Une seule ligne d'étiquettes où les catégories externes et internes alternent sans regroupement visuel | Étiquettes de catégorie multi-niveaux |
| Une série s'aplatit en une ligne | Deux séries diffèrent de plusieurs ordres de grandeur (ventes en millions, croissance en pourcentage) mais partagent un seul axe des valeurs | La plus petite série se comprime vers zéro et sa variation est invisible | Axe secondaire |
Aucun des deux n'est un problème de style. Dans les deux cas, l'axe ignore quelque chose qu'il doit savoir — que les catégories comportent des niveaux, ou que les valeurs ont des échelles incompatibles. Les deux sections ci-dessous les traitent tour à tour, et la seconde s'appuie sur la première afin que le graphique final intègre les deux corrections.
Prérequis
Vous avez besoin d'un projet React avec Spire.XLS for JavaScript installé et le module WebAssembly initialisé, accessible via window.wasmModule.spirexls. L'exemple charge une police et un fichier de données prédéfini dans le VFS avant de créer le graphique, et les deux sont récupérés depuis le dossier public du projet.
Les données derrière les étiquettes multi-niveaux
Les étiquettes multi-niveaux ne sont pas créées par une simple propriété : elles sont lues à partir des données. L'axe des catégories dessine autant de niveaux d'étiquettes qu'il y a de colonnes dans la plage vers laquelle pointe CategoryLabels. La feuille de calcul doit donc être organisée avec la hiérarchie répartie sur les colonnes :
| Colonne A (externe) | Colonne B (interne) | Colonne C (valeurs) |
|---|---|---|
| Nord | Janv. | 120 000 |
| Nord | Févr. | 135 000 |
| Sud | Janv. | 98 000 |
| Sud | Févr. | 110 000 |
Les étiquettes externes de la colonne A sont fusionnées sur les lignes qu'elles couvrent — « Nord » s'étend sur les deux lignes de janv. et févr. C'est cette fusion qui fait que le niveau se réduit visuellement à une seule étiquette par groupe au moment où le graphique est rendu. Sans elle, l'axe affiche toujours deux niveaux, mais le niveau externe répète l'étiquette sur chaque ligne au lieu de regrouper.
Il s'agit d'une question de disposition des données, non d'API de graphique. Le code du graphique doit seulement faire pointer CategoryLabels vers les deux colonnes ; le fait que les cellules externes soient fusionnées se décide dans le classeur, pas dans l'objet graphique.
Créer un graphique avec des étiquettes de catégorie multi-niveaux
Une fois les données disposées, le code du graphique fait deux choses : il fait pointer CategoryLabels vers une plage couvrant à la fois la colonne externe et la colonne interne, et il active MultiLevelLable pour que l'axe développe ces colonnes en lignes empilées. Les étapes sont les suivantes :
- Chargez la police et le fichier de données de test dans le VFS.
- Chargez le classeur et récupérez la feuille de calcul.
- Ajoutez un graphique à colonnes et ajoutez une série de ventes nommée.
- Faites pointer les étiquettes de catégorie vers la colonne de la région et celle du mois.
- Activez les étiquettes multi-niveaux pour l'axe des catégories et enregistrez le classeur.
function App() {
const createMultiLevelChart = async () => {
// Get the Spire.XLS WASM module
const xlsModule = window.wasmModule?.spirexls;
// Check if the module is ready
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// Load the font and the test data file into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'MultiLevelChartData.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// Load the workbook and get the first worksheet
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
const sheet = workbook.Worksheets.get(0);
// Add a column chart
const chart = sheet.Charts.Add({ chartType: xlsModule.ExcelChartType.ColumnClustered });
chart.ChartTitle = "Sales";
chart.Legend.Delete();
// Add the sales series and give it a name
const serie = chart.Series.Add({ name: "Sales", serieType: xlsModule.ExcelChartType.ColumnClustered });
serie.Values = sheet.Range.get("C2:C7");
// Point the category labels at both the region and the month column
serie.CategoryLabels = sheet.Range.get("A2:B7");
// Turn on multi-level category labels so each level gets its own row
chart.PrimaryCategoryAxis.MultiLevelLable = true;
// Place the chart on the worksheet
chart.LeftColumn = 5;
chart.TopRow = 1;
chart.RightColumn = 14;
// Save the workbook
const outputFileName = "MultiLevelLabels.xlsx";
workbook.SaveToFile({ fileName: outputFileName });
// Dispose of the workbook object to free resources
workbook.Dispose();
// Read the result file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Multi-Level Labels</h1>
<button onClick={createMultiLevelChart}>Start</button>
</div>
);
}
export default App;
Un graphique avec des étiquettes de catégorie multi-niveaux, chaque niveau sur sa propre ligne

C'est la plage A2:B7 qui fait afficher deux niveaux à l'axe. Lier une plage d'une seule colonne telle que B2:B7 ne produirait toujours qu'un seul niveau, même avec MultiLevelLable défini sur true — la propriété contrôle si plusieurs niveaux sont développés en lignes, et non si un niveau de données existe à développer.
Pourquoi la série de croissance disparaît
Ajoutez une seconde série pour la croissance d'une année sur l'autre — des valeurs autour de la dizaine, en pourcentage — et tracez-la sur le même axe des valeurs que les ventes. Les colonnes de ventes atteignent 120 000 ; le taux de croissance atteint 12. Sur un axe gradué de 0 à 140 000, le nombre 12 est indiscernable de zéro. La série est bien là, les données sont correctes, et le graphique affiche une ligne plate collée à la ligne de base.
Ce n'est ni un bug des données ni un bug du graphique. C'est l'axe des valeurs qui fait son travail — mapper une plage qui couvre la plus grande série — au détriment de la plus petite. La seule façon de bien voir les deux séries est de donner à chacune sa propre échelle, et c'est ce que fait l'axe secondaire.
Déplacer une série vers l'axe secondaire
La série de croissance est ajoutée sous forme de courbe plutôt que de colonne. Une courbe n'occupe aucune largeur de barre, elle se lit donc clairement face à la série de colonnes qui partage les mêmes catégories. La sortir de l'axe principal tient à une seule propriété : UsePrimaryAxis = false. Les étapes sont les suivantes :
- Chargez la police et le fichier de données de test dans le VFS.
- Chargez le classeur et récupérez la feuille de calcul.
- Ajoutez un graphique à colonnes et ajoutez une série de ventes nommée.
- Ajoutez la série de croissance sous forme de courbe.
- Déplacez la série de croissance vers l'axe secondaire et enregistrez le classeur.
function App() {
const addSecondaryAxis = async () => {
// Get the Spire.XLS WASM module
const xlsModule = window.wasmModule?.spirexls;
// Check if the module is ready
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// Load the font and the test data file into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'MultiLevelChartData.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// Load the workbook and get the first worksheet
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
const sheet = workbook.Worksheets.get(0);
// Add a column chart
const chart = sheet.Charts.Add({ chartType: xlsModule.ExcelChartType.ColumnClustered });
chart.ChartTitle = "Sales and YoY Growth";
// Add the sales series, which stays on the primary axis
const salesSerie = chart.Series.Add({ name: "Sales", serieType: xlsModule.ExcelChartType.ColumnClustered });
salesSerie.Values = sheet.Range.get("C2:C7");
// Point the category labels at both the region and the month column
salesSerie.CategoryLabels = sheet.Range.get("A2:B7");
// Add the growth series as a line
const growthSerie = chart.Series.Add({ name: "YoY Growth", serieType: xlsModule.ExcelChartType.Line });
growthSerie.Values = sheet.Range.get("D2:D7");
// Move the growth series to the secondary axis so it plots on its own percentage scale
growthSerie.UsePrimaryAxis = false;
// Turn on multi-level category labels
chart.PrimaryCategoryAxis.MultiLevelLable = true;
// Place the chart on the worksheet
chart.LeftColumn = 5;
chart.TopRow = 1;
chart.RightColumn = 14;
// Save the workbook
const outputFileName = "SecondaryAxis.xlsx";
workbook.SaveToFile({ fileName: outputFileName });
// Dispose of the workbook object to free resources
workbook.Dispose();
// Read the result file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Secondary Axis</h1>
<button onClick={addSecondaryAxis}>Start</button>
</div>
);
}
export default App;
Un graphique à colonnes avec un axe secondaire pour la série de courbe du taux de croissance

UsePrimaryAxis = false n'affecte que la série sur laquelle il est défini ; toutes les autres séries restent sur l'axe principal. Le graphique gagne une seconde paire d'axes des valeurs et des catégories, ce qui lui donne deux plages d'échelle distinctes. Series.Add prend le nom de la série au même moment, de sorte que la légende affiche le nom transmis plutôt qu'un « Série 1 » généré automatiquement.
Définir l'échelle de l'axe secondaire
Une fois qu'une série passe sur l'axe secondaire, cet axe calcule sa propre échelle — et il le fait indépendamment de l'axe principal. Les deux plages ne se connaissent pas, ce qui signifie que l'axe secondaire peut choisir des bornes qui ne s'accordent pas bien avec les données.
PrimaryValueAxis.MinValue, MaxValue et MajorUnit ne contrôlent que l'axe principal. Pour définir l'échelle de l'axe secondaire, utilisez SecondaryValueAxis :
// Give the secondary axis a 0-20 scale with a major unit of 5
chart.SecondaryValueAxis.MinValue = 0;
chart.SecondaryValueAxis.MaxValue = 20;
chart.SecondaryValueAxis.MajorUnit = 5;
Définissez l'échelle après avoir déplacé la série sur l'axe secondaire. Tant qu'aucune série n'utilise l'axe secondaire, l'affectation est acceptée mais jamais écrite dans le fichier — l'axe n'existe pas dans le résultat tant qu'une série n'y est pas tracée.
Problèmes courants
L'axe des catégories n'affiche qu'un seul niveau d'étiquettes.
CategoryLabels pointe vers une plage d'une seule colonne. Le nombre de niveaux est déterminé par le nombre de colonnes que couvre la plage, et non par la propriété MultiLevelLable. Pointez vers une plage multi-colonnes telle que A2:B7, et assurez-vous que les cellules d'étiquettes externes sont fusionnées dans les données.
L'échelle de l'axe secondaire semble incorrecte.
Les axes des valeurs principal et secondaire calculent leurs échelles indépendamment. Définir MinValue ou MaxValue sur PrimaryValueAxis n'affecte pas l'axe secondaire. Utilisez chart.SecondaryValueAxis pour définir directement son échelle, et faites-le après avoir déplacé une série dessus.
La série de croissance apparaît toujours plate après l'ajout d'un axe secondaire.
Vérifiez que UsePrimaryAxis = false est défini sur la série de croissance, et non sur la série de ventes. La propriété s'applique série par série — la définir sur la mauvaise série déplace la mauvaise vers l'axe secondaire.
La légende affiche « Série 1 » au lieu du nom de la série.
Le nom n'a pas été transmis à Series.Add. Utilisez chart.Series.Add({ name: "Sales", ... }) afin que la légende reprenne le nom souhaité plutôt qu'une étiquette générée automatiquement.
FAQ
Puis-je avoir plus de deux niveaux d'étiquettes de catégorie ?
Oui. Le nombre de niveaux est déterminé par le nombre de colonnes que couvre la plage CategoryLabels. Une plage de trois colonnes produit trois niveaux — par exemple, année, trimestre et mois. Les cellules d'étiquettes externes doivent être fusionnées dans les données pour que chaque niveau se regroupe correctement.
L'axe secondaire fonctionne-t-il avec des types de graphiques autres que les colonnes et les courbes ?
Oui. L'axe secondaire n'est pas lié à un type de graphique précis. Le schéma courant est colonnes plus courbe — la courbe n'occupe aucune largeur de barre et se lit clairement face aux colonnes — mais n'importe quelle série peut être déplacée vers l'axe secondaire en définissant UsePrimaryAxis = false.
Dois-je installer Excel pour créer ces graphiques ?
Non. Le moteur de feuille de calcul est fourni avec le package et s'exécute en WebAssembly dans le navigateur. Le classeur est créé, mis en graphique et enregistré entièrement côté client.
Puis-je contrôler séparément l'axe des catégories secondaire ?
Lorsqu'une série passe sur l'axe secondaire, le graphique gagne un axe des catégories secondaire en plus de l'axe des valeurs secondaire. Les deux axes des catégories partagent les mêmes étiquettes de catégorie par défaut, de sorte que les étiquettes multi-niveaux s'appliquent aux deux.
Le fichier de sortie est-il compatible avec Excel ?
Oui. Le classeur est enregistré au format .xlsx, et le graphique — y compris les étiquettes multi-niveaux et l'axe secondaire — est écrit sous forme de XML de graphique standard qu'Excel lit nativement.