
Un gráfico de columnas con regiones y meses en el mismo eje tiene dos problemas, y no son el mismo problema. El primero es que las etiquetas de categoría se colapsan en una sola fila — "North", "Jan", "North", "Feb" — y el lector tiene que reagrupar mentalmente qué mes pertenece a qué región. El segundo es que cuando se añade una serie de tasa de crecimiento junto a una serie de ventas que llega a los millones, la tasa de crecimiento se convierte en una línea plana pegada a la línea base, porque un solo eje de valores no puede servir a dos magnitudes a la vez.
Las etiquetas de categoría de varios niveles solucionan lo primero. Un eje secundario soluciona lo segundo. Son características independientes que resultan útiles en el mismo gráfico, y Spire.XLS for JavaScript maneja ambas a través de la API de ejes del gráfico — directamente en el navegador sobre WebAssembly, con archivos que circulan por un sistema de archivos virtual (VFS) y sin necesidad de backend.
Para la configuración del proyecto, consulte Integrating Spire.XLS for JavaScript in a React Project. Los siguientes ejemplos asumen que el paquete está instalado y que el módulo WebAssembly se ha inicializado.
Cuando un eje no es suficiente
Los dos problemas aparecen en el mismo tipo de hoja de cálculo — una en la que las categorías tienen una jerarquía y los valores tienen una dispersión — pero provienen de lugares diferentes:
| Problema | De dónde viene | Cómo se ve el gráfico | Qué lo soluciona |
|---|---|---|---|
| Las etiquetas se amontonan en una sola fila | Las categorías son jerárquicas (región → mes, año → trimestre) pero el eje las trata como planas | Una sola fila de etiquetas donde las categorías externas e internas se alternan sin agrupación visual | Etiquetas de categoría de varios niveles |
| Una serie se aplana hasta convertirse en una línea | Dos series difieren en órdenes de magnitud (ventas en millones, crecimiento en porcentaje) pero comparten un solo eje de valores | La serie más pequeña se comprime hasta casi cero y su variación resulta invisible | Eje secundario |
Ninguno de los dos es un problema de estilo. Ambos tienen que ver con que el eje no sabe algo que necesita saber — que las categorías tienen capas, o que los valores tienen escalas incompatibles. Las dos secciones siguientes los abordan por turnos, y la segunda se apoya en la primera para que el gráfico final incluya ambas correcciones.
Requisitos previos
Necesita un proyecto de React con Spire.XLS for JavaScript instalado y el módulo WebAssembly inicializado, accesible en window.wasmModule.spirexls. El ejemplo carga una fuente y un archivo de datos preconstruido en el VFS antes de crear el gráfico, y ambos se obtienen de la carpeta pública del proyecto.
Los datos detrás de las etiquetas de varios niveles
Las etiquetas de varios niveles no se crean solo con una propiedad — se leen de los datos. El eje de categorías dibuja tantos niveles de etiquetas como columnas haya en el rango al que apunta CategoryLabels. Por lo tanto, la hoja de cálculo debe estar organizada con la jerarquía distribuida en columnas:
| Columna A (externa) | Columna B (interna) | Columna C (valores) |
|---|---|---|
| North | Jan | 120,000 |
| North | Feb | 135,000 |
| South | Jan | 98,000 |
| South | Feb | 110,000 |
Las etiquetas externas de la columna A están combinadas a lo largo de las filas que abarcan — "North" abarca las dos filas de Jan y Feb. Esa combinación es lo que hace que el nivel se colapse visualmente en una sola etiqueta por grupo cuando se representa el gráfico. Sin ella, el eje sigue mostrando dos niveles, pero el nivel externo repite la etiqueta en cada fila en lugar de agrupar.
Esto es una cuestión de diseño de datos, no de la API de gráficos. El código del gráfico solo tiene que apuntar CategoryLabels a ambas columnas; si las celdas externas están combinadas se decide en el libro de trabajo, no en el objeto del gráfico.
Crear un gráfico con etiquetas de categoría de varios niveles
Una vez organizados los datos, el código del gráfico hace dos cosas: apunta CategoryLabels a un rango que abarca tanto la columna externa como la interna, y activa MultiLevelLable para que el eje expanda esas columnas en filas apiladas. Los pasos son:
- Cargar la fuente y el archivo de datos de prueba en el VFS.
- Cargar el libro de trabajo y obtener la hoja de cálculo.
- Añadir un gráfico de columnas y añadir una serie de ventas con nombre.
- Apuntar las etiquetas de categoría tanto a la columna de región como a la de mes.
- Activar las etiquetas de varios niveles para el eje de categorías y guardar el libro de trabajo.
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 gráfico con etiquetas de categoría de varios niveles, cada nivel en su propia fila

El rango A2:B7 es lo que hace que el eje muestre dos niveles. Enlazar un rango de una sola columna como B2:B7 seguiría produciendo un solo nivel incluso con MultiLevelLable establecido en true — la propiedad controla si se expanden varios niveles en filas, no si existe un nivel de datos que expandir.
Por qué desaparece la serie de crecimiento
Añada una segunda serie para el crecimiento interanual — valores en la decena baja, porcentajes — y represéntela en el mismo eje de valores que las ventas. Las columnas de ventas alcanzan 120,000; la tasa de crecimiento alcanza 12. En un eje que va de 0 a 140,000, el número 12 es indistinguible de cero. La serie está ahí, los datos son correctos, y el gráfico muestra una línea plana pegada a la línea base.
Esto no es un error en los datos ni en el gráfico. Es el eje de valores haciendo su trabajo — mapear un rango que cubre la serie más grande — a costa de la más pequeña. La única forma de ver claramente ambas series es darle a cada una su propia escala, y eso es lo que hace el eje secundario.
Mover una serie al eje secundario
La serie de crecimiento se añade como línea en lugar de como columna. Una línea no ocupa ancho de barra, así que se lee con claridad frente a la serie de columnas que comparte las mismas categorías. Moverla fuera del eje primario es una sola propiedad: UsePrimaryAxis = false. Los pasos son:
- Cargar la fuente y el archivo de datos de prueba en el VFS.
- Cargar el libro de trabajo y obtener la hoja de cálculo.
- Añadir un gráfico de columnas y añadir una serie de ventas con nombre.
- Añadir la serie de crecimiento como línea.
- Mover la serie de crecimiento al eje secundario y guardar el libro de trabajo.
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 gráfico de columnas con un eje secundario para la serie de línea de la tasa de crecimiento

UsePrimaryAxis = false afecta solo a la serie en la que se establece; todas las demás series permanecen en el eje primario. El gráfico gana un segundo par de ejes de valores y de categorías, lo que le da dos rangos de escala separados. Series.Add toma el nombre de la serie al mismo tiempo, así que la leyenda muestra el nombre pasado en lugar de un "Series 1" autogenerado.
Configurar la escala del eje secundario
Una vez que una serie se mueve al eje secundario, ese eje calcula su propia escala — y lo hace de forma independiente del primario. Los dos rangos no conocen nada el uno del otro, lo que significa que el eje secundario puede elegir límites que no se alinean bien con los datos.
PrimaryValueAxis.MinValue, MaxValue y MajorUnit controlan solo el eje primario. Para establecer la escala del eje secundario, utilice 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;
Establezca la escala después de que la serie se haya movido al eje secundario. Mientras ninguna serie utilice el eje secundario, la asignación se acepta pero nunca se escribe en el archivo — el eje no existe en la salida hasta que se representa una serie sobre él.
Problemas comunes
El eje de categorías muestra solo un nivel de etiquetas.
CategoryLabels está apuntando a un rango de una sola columna. El número de niveles lo decide cuántas columnas abarca el rango, no la propiedad MultiLevelLable. Apunte a un rango de varias columnas como A2:B7, y asegúrese de que las celdas de etiqueta externas estén combinadas en los datos.
La escala del eje secundario parece incorrecta.
Los ejes de valores primario y secundario calculan sus escalas de forma independiente. Establecer MinValue o MaxValue en PrimaryValueAxis no afecta al eje secundario. Utilice chart.SecondaryValueAxis para establecer su escala directamente, y hágalo después de mover una serie a él.
La serie de crecimiento sigue apareciendo plana tras añadir un eje secundario.
Compruebe que UsePrimaryAxis = false esté establecido en la serie de crecimiento, no en la serie de ventas. La propiedad es por serie — establecerla en la serie equivocada mueve la serie equivocada al eje secundario.
La leyenda muestra "Series 1" en lugar del nombre de la serie.
El nombre no se pasó a Series.Add. Utilice chart.Series.Add({ name: "Sales", ... }) para que la leyenda tome el nombre que pretendía en lugar de una etiqueta autogenerada.
Preguntas frecuentes
¿Puedo tener más de dos niveles de etiquetas de categoría?
Sí. El número de niveles lo determina el número de columnas que abarca el rango de CategoryLabels. Un rango de tres columnas produce tres niveles — por ejemplo, año, trimestre y mes. Las celdas de etiqueta externas deben estar combinadas en los datos para que cada nivel se agrupe correctamente.
¿Funciona el eje secundario con tipos de gráfico distintos de columnas y líneas?
Sí. El eje secundario no está vinculado a un tipo de gráfico específico. El patrón habitual es columnas más línea — la línea no ocupa ancho de barra y se lee con claridad frente a las columnas — pero cualquier serie puede moverse al eje secundario estableciendo UsePrimaryAxis = false.
¿Necesito tener Excel instalado para crear estos gráficos?
No. El motor de hojas de cálculo viene con el paquete y se ejecuta como WebAssembly en el navegador. El libro de trabajo se construye, se grafica y se guarda por completo en el lado del cliente.
¿Puedo controlar el eje de categorías secundario por separado?
Cuando una serie se mueve al eje secundario, el gráfico gana un eje de categorías secundario además del eje de valores secundario. Los dos ejes de categorías comparten las mismas etiquetas de categoría de forma predeterminada, así que las etiquetas de varios niveles se aplican a ambos.
¿Es el archivo de salida compatible con Excel?
Sí. El libro de trabajo se guarda como .xlsx, y el gráfico — incluidas las etiquetas de varios niveles y el eje secundario — se escribe como XML de gráfico estándar que Excel lee de forma nativa.