This guest post was written by Zara Cooper. She is a web developer and technical writer. You can get in touch with her through her website.
Apache ECharts is an open-source visualization library that is written in JavaScript. This library offers twenty different types of charts from basic ones, like pie, line, and bar charts, to more complex ones, like GEO/map, sankey, tree, and sunburst. It also bundles about a dozen utility components, like tooltip, brushing, and dataZoom that can be combined to augment the charts. It is optimized for mobile devices, prioritizes accessibility, and can adequately display large, intricate quantities of data.
In this tutorial, you will learn how to integrate Cube with an Angular 13 app. The app will use Apache ECharts to visualize query results from Cube. You will set up a database, add test data to it, run a Cube instance within a Docker container, and connect it to the database you made. Next, you will generate an Angular 13 app, add Apache ECharts to it, make queries to the Cube instance, and display the results using the ECharts. At the end of the tutorial, your Angular 13 app should look like this:
More on Cube
Cube is an open-source analytical API platform. It helps you create data applications and reporting tools faster. This can be achieved either with a local instance or on the cloud. It acts as a headless API layer that frontend applications can use to interface with data warehouses, and it can connect to different types of data stores, like MongoDB, MySQL, and PostgreSQL. On its playground, you can create and test your projects, generate custom frontend interfaces, and analyze and improve your queries. Additionally, a secure REST API is made available for your apps to use when interacting with Cube. To learn more about Cube, you can head over to its website or read their documentation.
More on Apache ECharts
One of the best uses for Apache ECharts is displaying complex data that requires a more nuanced representation. With its wide range of charts, it can present a broad spectrum of data with varying characteristics. It is a great choice for handling substantial amounts of data as it is optimized for incremental rendering and data streaming.
Apache ECharts also has a large and active open-source community behind it that continually works to add to and improve it. ECharts implements the Web Accessibility Initiative - Accessible Rich Internet Applications (WAI-ARIA) specification, ensuring that users with disabilities can better access and interact with its charts. It is optimized for use on mobile devices; offers cross-platform support; and works with multi-dimensional, multi-format, and dynamic data.
A major downside of using ECharts is that it does not provide official libraries for various frontend web frameworks and mobile platforms, complicating the integration process.
Compared to other charting libraries, like Chart.js, however, ECharts offers a more extensive list of chart types, components, and features with a greater degree of customization.
It’s a fairly straightforward process to integrate ECharts with Cube. To start, you’d have to format the Cube query results into chart data that ECharts can accept. Then all that’s left to do is specify it as a property in the customization options for the chart, and ECharts handles the rest.
Tutorial
As explained above, this tutorial will walk you through how to integrate Cube with an Apache ECharts Angular 13 app. Let’s get started.
Use Case Theme
To demonstrate how to use Apache ECharts with Cube in this article, you will use college major graduate data from FiveThirtyEight. Specifically, you will use this women-stem.csv
data, which you’ll download later on.
Prerequisites
To follow along with this tutorial, you will need the following:
- Node.js: You will use this to install the Angular CLI. This tutorial was made using Node.js v16.10.0. The Node.js Download page offers pre-built installers for various OSs.
- MongoDB Community Server: This will be the database you will use with Cube. This tutorial uses MongoDB v5.0.2. This manual provides instructions on how to install it for your particular OS.
- MongoDB Connector for BI: This is what Cube will use to interface with MongoDB. Cube uses SQL queries, and this connector translates them for MongoDB. You can find install instructions for your specific OS here.
- Angular CLI: You will use this to generate the Angular app. This tutorial was created using Angular CLI v13.1.2. The Angular CLI Documentation page will guide you through installing it.
- Docker Desktop: You will use Docker to run the containerized Cube.js app. This "Getting Started" guide on the Docker website will walk you through how to install it on your specific OS. This tutorial was made using Docker Desktop v 4.5.0.
Cube Setup
In this section, you will set up a Mongo database and user, run the MongoDB BI Connector process, and set up the Cube app. Cube will interface with the connector process to get data from your MongoDB database. This section shows you how to run a Cube instance within a Docker container. If you would like to run it in the cloud instead, you can follow these deployment guides available on Cube’s documentation site, or use Cube Cloud.
Step 1: Set Up a Mongo User
When setting up a Cube app, you’re required to provide information about the database you will connect to. This information includes a database host, port, username, and password. The user whose credentials you provide should have read access to your database.
In this step, you will create a user that the Cube app will use to access your database. To begin, start MongoDB. Refer to the installation instructions for your specific OS to do so. Once it’s started, launch the MongoDB Shell by running this:
mongosh
On the shell, set the current database to admin
:
use admin
Then create a new user with the username cube
and a password of your choosing. Run this command to create them and enter a suitable password:
db.createUser(
{
user: "cube",
pwd: passwordPrompt(),
roles: [ { role: "read", db: "collegeMajors" } ]
}
)
This command will give read permissions to the cube
user on the collegeMajors
database, which will hold the college majors data.
Ensure that MongoDB is running throughout the tutorial.
Step 2: Import Data into the Database
In this step, you will download the college majors of women in STEM data, women-stem.csv
, from GitHub. You will then import this data to the collegeMajors
database using mongoimport
. Begin by creating a folder where the women-stem.csv
data file, the Cube app, and the Angular app will reside:
mkdir college-majors && cd college-majors
In this folder, download the data from GitHub by running the following:
curl https://raw.githubusercontent.com/fivethirtyeight/data/master/college-majors/women-stem.csv --output women-stem.csv
Next, import the data into the collegeMajors
database with this command:
mongoimport --type csv --db collegeMajors --collection womenStem --file women-stem.csv --headerline
All the data from the CSV file are now imported into the womenStem
collection of the collegeMajors
database.
Step 3: Run the Cube App
In this step, you will run the containerized Cube.js app. Begin by making sure that Docker is running. Next, within the college-majors
folder, create a college-majors-cube
folder where the Cube schemas and environment configuration will reside.
cd college-majors
mkdir college-majors-cube
cd college-majors-cube
Mac and Windows users can use this command to run Cube:
docker run --name my-first-cube \
-p 4000:4000 --rm \
-v ${PWD}:/cube/conf \
-e CUBEJS_DEV_MODE=true \
cubejs/cube
Linux users can run Cube with this command:
docker run --name my-first-cube \
-p 4000:4000 --rm \
--net=host
-v ${PWD}:/cube/conf \
-e CUBEJS_DEV_MODE=true \
cubejs/cube
The --name
flag names the newly running container my-first-cube
. You'll publish the container's 4000
port to the host's 4000
port. With the -rm
flag, you specify that the container should automatically be removed when it exits. The -v
flag mounts the current working directory (PWD
) into the /cube/conf
folder in the container. The -e
flag will set the CUBEJS_DEV_MODE=true
environment variable to ensure that Cube is running in development mode, which enables the Developer Playground, among other things. For Linux users, the –net
flag connects the container to the host’s network so that it can connect to mongosqld
.
Running this command will create two folders within college-majors-cube
: schema
and .cubestore
. The schema
is where all the schema files will be stored once you create them.
Step 4: Launch the MongoDB BI Connector mongosqld
Process
In this step, you will start the MongoDB BI Connector process, mongosqld
. The MongoDB BI Connector serves as a connection, which translates data and queries, between a MongoDB instance and an SQL business intelligence (BI) tool/client. These BI tools/clients are typically used for reporting on and visualizing data from the MongoDB database. One of its components, mongosqld
, acts as a proxy that receives SQL queries from an SQL client/tool and relays them to a MongoDB instance.
While there are various ways to run mongosqld
, you would typically start this process by running the command below on your terminal:
mongosqld
Leave this process running throughout the tutorial.
Step 5: Connect the MongoDB Database to the Cube App
Now that the mongosqld
process is running, you can head to the Cube app. It can now be accessed at http://localhost:4000.
When you visit http://localhost:4000 the first time you run the Cube app, you will be prompted to connect to a MongoDB database. This will happen on the http://localhost:4000/#/connection page, which looks like this:
Input these values on the form and then click the Apply button:
Mac and Windows Users:
Form Item | Value |
---|---|
Hostname | host.docker.internal |
Port (that mongosqld is running on) |
3307 |
Username | cube |
Password | [The password you entered when creating the MongoDB user] |
Database | collegeMajors |
Linux Users:
Form Item | Value |
---|---|
Hostname | localhost |
Port (that mongosqld is running on) |
3307 |
Username | cube |
Password | [The password you entered when creating the MongoDB user] |
Database | collegeMajors |
Mac and Windows users will use host.docker.internal
for the database hostname to connect the container with mongosqld
on the host. This DNS name resolves to the internal IP address of the host.
Once the connection to the database is successfully made, you will be redirected to http://localhost:4000/#/schema, where you can view your Cube data schemas.
You’ve now successfully created a MongoDB database and user, added data to the database, set up the Cube app, and run it. In the next section, you’ll get a brief overview of the Cube playground, generate data schemas, and run some queries against the college majors’ data.
Cube Playground
The Cube playground is a web tool that allows you to generate data schemas for your data. With it, you can also run queries against this data and visualize the query results in various forms, like raw numbers, tables, and charts. The playground also provides a utility for generating analytics dashboards. These are built using a range of frontend frameworks, like React and Angular, among others.
The playground is split into three tabs: You build queries on the Build tab, generate analytics dashboards on the Dashboard App tab, and create data schemas on the Schema tab.
During this portion of the tutorial, you will set up schemas and run a sample query on the playground.
Step 1: Create a WomenStem.js
Schema
Head over to http://localhost:4000/#/schema. On the left-side navigation, under the Tables tab, you should see the collegeMajors database listed. Click on it and you should see the womenStem collection. Tick it and click the Generate Schema button.
You should see this pop-up message displayed once the schema is generated:
The side navigation will then switch to the Files tab, which will show the newly generated WomenStem.js
file.
Step 2: Modify the WomenStem.js
Schema
The WomenStem.js
schema file is located at college-majors-cube/schema/WomenStem.js
in the college-majors-cube
folder. In this step, you will modify it to include additional measures. A measure is data you can quantify, such as the number of male and female graduates. A dimension is data you can categorize, like a major or a major category. The WomenStem.js
schema currently has two measures: count
, which is a type of measure that counts table records, and total
, which corresponds to the total number of graduates. The WomenStem.js
schema also has two dimensions: major
and majorCategory
.
You will add two measures to the WomenStem.js
schema: women
and men
. They will be of the sum
type, meaning that they will be a summation of the women
and men
fields. In the college-majors-cube/schema/WomenStem.js
file, change the measures
property:
measures: {
count: { type: `count`, drillMembers: [] },
total: { sql: `${CUBE}.\`Total\``, type: `sum` },
men: { sql: `${CUBE}.\`Men\``, type: `sum` },
women: { sql: `${CUBE}.\`Women\``, type: `sum` }
},
Step 3: Build a Query
You can use the Build tab to test out and experiment with queries. You can later use these queries in your frontend dashboard. For instance, if you wanted to model the number of female graduates in various major categories, you could easily do that on the playground. All you’d have to do is select women
as a measure, major-category
as a dimension, and a way to view the results. Here’s a screenshot of this query with its results displayed in a bar chart:
In this section of the tutorial, you generated a WomenStem.js
schema using data from the collegeMajors
database, modified the schema to include additional measures, and built a query that you will use in the next phase.
Angular 13 Dashboard with Apache ECharts
In this last segment of the tutorial, you will generate an Angular 13 dashboard app, add the Apache ECharts library to it, and create charts that will visualize query results from the Cube app.
Step 1: Generate the Angular 13 Dashboard
To create the Angular 13 dashboard, start by changing directories to the college-majors
folder and call the app college-majors-dashboard
. To generate the app, run this command on your terminal:
ng new college-majors-dashboard --skip-tests --minimal
When prompted to add Angular routing, select No. Pick CSS for styling in the prompt that follows.
ng new college-majors-dashboard --minimal --skip-tests
Would you like to add Angular routing? No
Which stylesheet format would you like to use? CSS
Step 2: Add Environment Variables, Dependencies, and Imports
To connect to the college-majors-cube
app, you will need to provide an API URL to it. If you are running in any other environment other than development, you also need to provide an API token that you will pass in an authorization header. Since the Cube app is running in development in this tutorial, you do not need to provide this token.
To add the Cube app API URL as an environment variable, replace the environment
constant in the college-majors-dashboard/src/environments/environment.ts
file with this instead:
export const environment = {
production: false,
cubeJSAPI: 'http://localhost:4000/cubejs-api/v1'
};
Apache ECharts does not provide an official Angular library, but a pretty reliable alternative is ngx-echarts, which is an Apache ECharts Angular directive. To install it, run the following command on your terminal within the college-majors-dashboard
directory:
cd college-majors-dashboard
npm install echarts ngx-echarts -S
Running this command will add both Apache ECharts and the ngx-echarts
directive to the app as dependencies.
Next, you will add three imports to AppModule
: NgxEchartsModule
, HttpClientModule
, and FormsModule
. NgxEchartsModule
will provide the Apache ECharts while the FormsModule
provides interactivity to the charts. HttpClientModule
will provide an HTTP client to make requests to the Cube app. Change the contents of src/app/app.module.ts
file to this:
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { NgxEchartsModule } from 'ngx-echarts';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
NgxEchartsModule.forRoot({
echarts: () => import('echarts'),
}),
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Step 3: Generate a College Majors Cube Service
While Cube provides an Angular module to integrate with Cube, it is not compatible with Angular 13, which is the current major version of Angular as of the writing of this tutorial. You may also face other compatibility issues with this module depending on what Angular version you use. So instead of using this, you will create an HTTP service that will make requests to the Cube app using its REST API.
The service will be called college-majors-cube
. To create it, run the following command on your terminal:
ng generate service college-majors-cube
This will result in a file created at src/app/college-majors-cube.service.ts
. Change the contents of this file:
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { map, Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
interface Query {
measures?: string[],
dimensions?: string[],
order?: object,
filters?: object[],
limit?: number
}
@Injectable({
providedIn: 'root'
})
class CollegeMajorsCubeService {
constructor(private http: HttpClient) { }
load(query: Query): Observable<object[]> {
return this.http.post<{ query: Query, data: object[] }>(`${environment.cubeJSAPI}/load`, { query })
.pipe(
map(resp => resp.data)
);
}
}
export { Query, CollegeMajorsCubeService };
In this service, HttpClient
is injected into the constructor. The Query
interface reflects what a Cube query looks like, for the most part. The load
method makes a POST request to the Cube app with the query.
Step 4: Add AppComponent
Methods to Format Chart Data and Make Queries
In this step, you’ll use AppComponent
to make Cube queries and display their results using Apache ECharts. To do so, you will modify AppComponent
.
To begin, change the contents of src/app/app.component.ts
to the following:
import { Component, OnInit } from '@angular/core';
import { EChartsOption } from 'echarts';
import { map, Observable, reduce, switchMap } from 'rxjs';
import { CollegeMajorsCubeService, Query } from './college-majors-cube.service';
interface ChartData {
xAxisData: string[],
seriesData: number[][],
seriesLabels: string[]
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
constructor(private cmcs: CollegeMajorsCubeService) { }
ngOnInit() {}
}
Here, new imports are added at the top of the file, which are used later in the file. The ChartData
interface models what the chart data should look like after it’s processed from the Cube query results. AppComponent
’s templates and styling are changed from in-line to URLs. The CollegeMajorsCubeService
is injected into the AppComponent
’s constructor. You’ll use it to load queries from the Cube app. Lastly, AppComponent
implements OnInit
. You’ll use its ngOnInit
method to make some initial queries.
Next, add a private formatBarChartData
method to AppComponent
:
private formatBarChartData(title = '', xAxisLabel = '', yAxisLabel = ''): (source$: Observable<ChartData>) => Observable<EChartsOption> {
return source$ => source$.pipe(
map(chartData => {
let options: EChartsOption = {
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
title: { text: title, show: true },
xAxis: { type: 'category', data: chartData.xAxisData, name: xAxisLabel, axisTick: { alignWithLabel: true } },
series: [],
yAxis: { type: 'value', name: yAxisLabel },
legend: { data: chartData.seriesLabels }
};
chartData.seriesData.forEach((series, index) => {
if (options.series && Array.isArray(options.series)) {
options.series.push({
type: 'bar',
data: series,
name: chartData.seriesLabels[index],
label: { show: true, rotate: 90, align: 'left', verticalAlign: 'middle', position: 'insideBottom', distance: 15, formatter: '{a} → {c}', fontSize: 14 }
});
}
});
return options;
})
);
}
The formatBarChartData
method takes ChartData
, creates an EChartOption
, and adds the chart data to it. An EChartOption
is used to set configuration for the chart and includes things like the chart title, its data, labels, and styling. In addition to adding chart data to options, this method also adds the title
, xAxisLabel
, and yAxisLabel
arguments that it receives. Lastly, it adds chart styling, tooltips, and a legend, and then returns the EChart options.
Next, add a getChartOptions
private method:
private getChartOptions(query: Query, title = '', xAxisLabel = '', yAxisLabel = '') {
return this.cmcs.load(query).pipe(
switchMap(data => data),
reduce((ac: ChartData, cv: object, index: number) => {
const vals = Object.values(cv);
if (index == 0) {
for (let i = 1; i < vals.length; i++) {
ac.seriesData.push([]);
}
ac.seriesLabels = Object.keys(cv).slice(1).map(k => k.substring(k.lastIndexOf('.') + 1));
}
ac.xAxisData.push(vals[0]);
for (let i = 1; i < vals.length; i++) {
ac.seriesData[i - 1].push(vals[i]);
}
return ac;
},
{ xAxisData: [], seriesData: [], seriesLabels: [] }
),
this.formatBarChartData(title, xAxisLabel, yAxisLabel)
);
}
The getChartOptions
method calls the CollegeMajorsCubeService.load
method using the query argument it receives. Once it receives the query results data, it reduces it to a ChartData
object. This object is then passed to the formatBarChartData
method, which returns an EChartOption
object that an Apache EChart can use.
AppComponent
will display four bar charts:
- Majors per category
- Graduates by gender in each major category
- Most popular majors in each gender group
- Most popular majors in each major category
To get data for the last two charts, you’ll need two public methods: getTopMajorsInGroup
and getTopMajorsInCategory
. selectedGroup
and selectedCategory
are properties that track which gender group and major category have been selected on the page. topGroupMajors$
and topCategoryMajors$
are observables that resolve the EChartOption
s for the last two charts. Copy all these below and add them to AppComponent
:
selectedGroup = 'total';
selectedCategory = 'Biology & Life Science';
topGroupMajors$!: Observable<EChartsOption>;
topCategoryMajors$!: Observable<EChartsOption>;
getTopMajorsInGroup() {
this.topGroupMajors$ = this.getChartOptions(
{
'measures': [
`WomenStem.${this.selectedGroup}`
],
'order': {
[`WomenStem.${this.selectedGroup}`]: 'desc'
},
'dimensions': [
'WomenStem.major'
],
'limit': 3
},
`Popular Majors in ${this.selectedGroup}`,
'Major',
'Number of Graduates'
);
}
getTopMajorsInCategory() {
this.topCategoryMajors$ = this.getChartOptions(
{
'measures': ['WomenStem.women', 'WomenStem.men', 'WomenStem.total'],
'order': { 'WomenStem.total': 'desc' },
'dimensions': ['WomenStem.major'],
'filters': [
{
'member': 'WomenStem.majorCategory',
'operator': 'equals',
'values': [this.selectedCategory]
}
],
'limit': 3
},
`Graduates in Top 3 Popular Majors in ${this.selectedCategory}`,
'Majors',
'Number of Graduates'
);
}
ngOnInit() {
this.getTopMajorsInGroup();
this.getTopMajorsInCategory();
}
Here, getTopMajorsInGroup
calls the getChartOptions
method with a query containing a gender group measure and a major dimension. It then assigns the resultant EChartOption
observable to topGroupMajors$
. getTopMajorsInCategory
calls the getChartOptions
method with a query containing all the gender groups as a measure and major as a dimension, and then filters the results by the selected major category. It assigns the EChartOption
observable it gets to the topCategoryMajors$
. Currently, the charts that use the options returned by topGroupMajors$
and topCategoryMajors$
are not displayed when the page loads; they are only shown when selections are made from the drop-downs. To remedy this, you’ll call both the getTopMajorsInGroup
and getTopMajorsInCategory
methods in the ngOnInit
method.
The first two charts have their options held by the majorsPerCategory$
and majorCategoriesByGender$
properties. Copy them below and add them to AppComponent
:
majorsPerCategory$ = this.getChartOptions(
{
'measures': ['WomenStem.count'],
'dimensions': ['WomenStem.majorCategory']
},
'Majors Per Category',
'Major Categories',
'Number of Majors'
);
majorCategoriesByGender$ = this.getChartOptions(
{
'measures': ['WomenStem.women', 'WomenStem.men', 'WomenStem.total'],
'dimensions': ['WomenStem.majorCategory']
},
'Graduates per Major Category by Gender',
'Major Categories',
'Number of Graduates'
);
Here, majorsPerCategory$
makes a query measuring the number of majors per major category, while majorCategoriesByGender$
measures the number of graduates per gender group in each major category.
Step 5: Add Template Content and Styling
In this step, you will add content to the AppComponent
template and style files. Start by creating these files on your terminal using the command below:
touch src/app/app.component.html src/app/app.component.css
Add this code to the src/app/app.component.html file
:
<h1>Apache ECharts with Cube.js Query Results</h1>
<h2>Bar Chart 1</h2>
<div *ngIf="majorsPerCategory$ | async as majorsPerCategory" echarts [options]="majorsPerCategory" class="cm-bar-chart">
</div>
<hr>
<h2>Bar Chart 2</h2>
<div *ngIf="majorCategoriesByGender$ | async as majorCategoriesByGender" echarts [options]="majorCategoriesByGender"
class="cm-bar-chart">
</div>
<hr>
<h2>Bar Chart 3</h2>
<label for="group">Select a group to show popular majors</label>
<select [(ngModel)]="selectedGroup" (ngModelChange)="getTopMajorsInGroup()" id="group" name="group">
<option *ngFor="let gr of ['women', 'men', 'total']" [ngValue]="gr">{{gr}}</option>
</select>
<div *ngIf="topGroupMajors$ | async as topGroupMajors" echarts [options]="topGroupMajors" class="cm-bar-chart">
</div>
<hr>
<h2>Bar Chart 4</h2>
<label for="group">Select a category to show popular majors</label>
<select [(ngModel)]="selectedCategory" (ngModelChange)="getTopMajorsInCategory()" id="group" name="group">
<option
*ngFor="let cat of ['Health', 'Engineering', 'Biology & Life Science', 'Computers & Mathematics', 'Physical Sciences']"
[ngValue]="cat">{{cat}}</option>
</select>
<div *ngIf="topCategoryMajors$ | async as topCategoryMajors" echarts [options]="topCategoryMajors" class="cm-bar-chart">
</div>
In this file, all four charts are included, and the chart options for them are specified. Two selects are also added so that new queries can be made for each different gender group or major category selected for the “Most popular majors in each gender group” and “Most popular majors in each major category” charts.
Lastly, add styling to src/app/app.component.css
:
.cm-bar-chart {
min-width: 90vw;
padding: 1rem;
margin: 3rem 1rem;
border: 1px solid lightgray;
}
h1, h2 {
margin: 3rem 1rem 0 1rem;
font-family: Arial;
}
select, label {
font-family: Arial;
font-size: 1.5rem;
padding: 0 1rem;
}
Step 6: Preview the Dashboard
Now that you’ve finished adding all the code you need for the dashboard, you can start the app. In your terminal, run the following command:
ng serve
The college majors dashboard will be served at http://localhost:4200. Check it out in your browser.
Conclusion
In this tutorial, you created a database, added sample data to it, generated a Cube app, and connected the database to it. You then created a data schema for the sample data, modified it to include additional measures, and made a test query on the Cube playground. Finally, you generated an Angular 13 app, added Apache ECharts to it, created a service to interface with the Cube REST API, and added four charts to visualize query results.
Please don't hesitate to like and bookmark this post, write a comment, and give a star to Cube on GitHub. I hope that you'll try Cube, Apache ECharts, and Angular in your next production gig or your next pet project.
The code for the frontend portion of this app can be found in this GitHub repo. The Cube WomenStem.js
schema file can be found in this gist. The college majors of women in STEM data, women-stem.csv
, by FiveThirtyEight is licensed under CC BY 4.0. This data was not modified.
Top comments (0)