- Developers
- Tutorials
- Integrate Charts and Conditional Rendering
Integrate Charts and Conditional Rendering
-
Join the conversation on Facebook
-
Join the conversation on Twitter
-
Subscribe to the YouTube Channel
-
Join the conversation on LinkedIn
-
View our projects on GitHub
-
Share via email
Integrate Charts and Conditional Rendering
You will learn
- How to install and import charts
- Learn about charts in UI5 web components
- How to add dynamic rendering
UI5 Web Components for React also comes with a chart library. In this tutorial, you will integrate two chart types and add data to them. Also you will learn how to conditionally render components, and how React handles updates to the DOM and single components.
-
Install the chart library of UI5 Web Components for React.
npm install @ui5/webcomponents-react-charts --save
-
Then, import
LineChart
andBarChart
intoMyApp.jsx
.import { BarChart, LineChart } from "@ui5/webcomponents-react-charts";
-
Start with the
LineChart
. You can add it underneath theText
component. Then pass thedimensions
andmeasures
prop with an empty array as value.<Text style={spacing.sapUiContentPadding}> This is the content area of the Card </Text> <LineChart measures={[]} dimensions={[]} />
Now you should see a placeholder for the
LineChart
within theCard
content. The reason for this is that the chart has not received any data and therefore the placeholder is displayed. -
Add data to your component (right above the
return
statement).const dataset = [ { month: "January", data: 65 }, { month: "February", data: 59 }, { month: "March", data: 80 }, { month: "April", data: 81 }, { month: "May", data: 56 }, { month: "June", data: 55 }, { month: "July", data: 40 } ];
-
Now add the
dataset
to yourLineChart
and configure thedimensions
andmeasures
props.<LineChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Stock Price" }]} dataset={dataset} />
Congratulation, you implemented your first Chart component.
-
Add a
BarChart
to theCard
.We want the same data just with a different representation, therefore you can use the same props as you did with the
LineChart
.<BarChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Stock Price" }]} dataset={dataset} />
Two charts are rendered now with equal datasets but different representation.
Your MyApp.jsx
component should look like this:
import React from "react";
import { Card, Text } from "@ui5/webcomponents-react";
import { spacing } from "@ui5/webcomponents-react-base";
import { BarChart, LineChart } from "@ui5/webcomponents-react-charts";
export function MyApp() {
const handleHeaderClick = () => {
alert("Header clicked");
};
const dataset = [
{
month: "January",
data: 65
},
{
month: "February",
data: 59
},
{
month: "March",
data: 80
},
{
month: "April",
data: 81
},
{
month: "May",
data: 56
},
{
month: "June",
data: 55
},
{
month: "July",
data: 40
}
];
return (
<div>
<Card
heading="Card"
style={{ width: "300px" }}
headerInteractive
onHeaderClick={handleHeaderClick}
>
<Text style={spacing.sapUiContentPadding}>
This is the content area of the Card
</Text>
<LineChart
dimensions={[{ accessor: "month" }]}
measures={[{ accessor: "data", label: "Stock Price" }]}
dataset={dataset}
/>
<BarChart
dimensions={[{ accessor: "month" }]}
measures={[{ accessor: "data", label: "Stock Price" }]}
dataset={dataset}
/>
</Card>
</div>
);
}
Two charts in one Card
is a bit too much, don’t you think? It would be nicer if the charts could be toggled by clicking on the header. Let’s implement that!
-
First add a state. It should control, which chart is going to be rendered. Use the State Hook logic to implement the state and set
"lineChart"
as default value. Don’t forget to importuseState
from React, otherwise you will get an error.-
Import the
useState
function in the header of theMyApp.jsx
file (replace the current import of React).import React, { useState } from "react";
-
Use the
useState
function in the right after you start to define theMyApp
function (before the click handler).const [toggleCharts, setToggleCharts] = useState("lineChart");
-
-
By clicking on the
Card
header the state should be set corresponding to the chart which should be displayed.Rewrite your
handleHeaderClick
function so it will handle this logic.const handleHeaderClick = () => { if (toggleCharts === "lineChart") { setToggleCharts("barChart"); } else { setToggleCharts("lineChart"); } };
-
To only render the current chart, add the following lines to the render of the component:
<Card heading="Card" style={{ width: "300px" }} headerInteractive onHeaderClick={handleHeaderClick}> <Text style={spacing.sapUiContentPadding}> This is the content area of the Card </Text> {toggleCharts === "lineChart" ? ( <LineChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Stock Price" }]} dataset={dataset} /> ) : ( <BarChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data" }]} dataset={dataset} /> )} </Card>
Done! Now you can toggle between charts by clicking on the header of the
Card
. -
You can further improve your
Card
component by using theavatar
prop
and adding anIcon
to it.Add the following import to your component:
import { Card, Text, Icon } from "@ui5/webcomponents-react";
And the
avatar
prop, which receives anIcon
as value, to theCard
component:<Card avatar={<Icon name="line-chart" />} ... </Card>
To reduce bundle size,
Icons
need to be imported manually. As we used aline-chart
add this to your imports.import '@ui5/webcomponents-icons/dist/line-chart.js';
The
Icons
should also be conditionally rendered. Luckily this is easy. First add thebar-chart
import:import '@ui5/webcomponents-icons/dist/horizontal-bar-chart.js';
Then change the
name
prop of theIcon
to the following:<Card avatar={ <Icon name={ toggleCharts === "lineChart" ? "line-chart" : "horizontal-bar-chart" } /> } ... >
Now the
Card
also changes theIcon
by clicking on the header.
If something went wrong you can compare your component to this code snippet:
import React, { useState } from "react";
import { Card, Text, Icon } from "@ui5/webcomponents-react";
import { spacing } from "@ui5/webcomponents-react-base";
import { BarChart, LineChart } from "@ui5/webcomponents-react-charts";
import "@ui5/webcomponents-icons/dist/line-chart.js";
import "@ui5/webcomponents-icons/dist/horizontal-bar-chart.js";
export function MyApp() {
const [toggleCharts, setToggleCharts] = useState("lineChart");
const handleHeaderClick = () => {
if (toggleCharts === "lineChart") {
setToggleCharts("barChart");
} else {
setToggleCharts("lineChart");
}
};
const dataset = [
{
month: "January",
data: 65
},
{
month: "February",
data: 59
},
{
month: "March",
data: 80
},
{
month: "April",
data: 81
},
{
month: "May",
data: 56
},
{
month: "June",
data: 55
},
{
month: "July",
data: 40
}
];
return (
<div>
<Card
avatar={
<Icon
name={
toggleCharts === "lineChart"
? "line-chart"
: "horizontal-bar-chart"
}
/>
}
heading="Card"
style={{ width: "300px" }}
headerInteractive
onHeaderClick={handleHeaderClick}
>
<Text style={spacing.sapUiContentPadding}>
This is the content area of the Card
</Text>
{toggleCharts === "lineChart" ? (
<LineChart
dimensions={[{ accessor: "month" }]}
measures={[{ accessor: "data", label: "Stock Price" }]}
dataset={dataset}
/>
) : (
<BarChart
dimensions={[{ accessor: "month" }]}
measures={[{ accessor: "data", label: "Stock Price" }]}
dataset={dataset}
/>
)}
</Card>
</div>
);
}
One of the main advantages of React is how UI updates are handled. React will only re-render the component if the state of the component has changed. So it will not update the whole UI, but only the component that is affected by changes.
-
In order to demonstrate this behavior, add a new
state
(right after the definition of the previous state).const [loading, setLoading] = useState(false);
-
Then edit your
handleHeaderClick
function like this:const handleHeaderClick = () => { if (toggleCharts === "lineChart") { setLoading(true); setTimeout(() => { setLoading(false); setToggleCharts("barChart"); }, 2000); } else { setLoading(true); setTimeout(() => { setLoading(false); setToggleCharts("lineChart"); }, 2000); } };
-
Add
loading
to both of your charts.<LineChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Stock Price" }]} dataset={dataset} loading={loading} />
<BarChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Stock Price" }]} dataset={dataset} loading={loading} />
This updates the component every time you switch between charts and simulates a data call.
As you can see, only the component affected by the state
is updated, and the rest stays the same. If you’re working with data, you most probably will need a loading indicator. All UI5 web components that are able to display data have a loading
prop and therefore also a loading indicator.
To make your Card
look cleaner and to give the user the information that the header is clickable, you can add some logic to your component.
-
Add a dynamic content
Text
The content text is not really informative. Let’s change that and display the type of the chart. Add the following constants to your component (after the event handler):
const contentTitle = toggleCharts === 'lineChart' ? 'Line Chart' : 'Bar Chart'; const switchToChart = toggleCharts === 'lineChart' ? 'Bar Chart' : 'Line Chart';
-
Change the title and add a subtitle to your
Card
First change the value of
heading
to something that explains the content of theCard
(e.g.,"Stock Price"
).
Then add asubheading
prop. Here you can give the users the information that they can switch between charts by clicking the header.<Card avatar={<Icon name={toggleCharts === 'lineChart' ? "line-chart": "horizontal-bar-chart"} />} heading="Stock Price" style={{ width: "300px" }} headerInteractive onHeaderClick={handleHeaderClick} subheading={`Click here to switch to ${switchToChart}`} > <Text style={spacing.sapUiContentPadding}>{contentTitle}</Text> {toggleCharts === "lineChart" ? ( <LineChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Stock Price" }]} dataset={dataset} loading={loading} /> ) : ( <BarChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Stock Price" }]} dataset={dataset} loading={loading} /> )} </Card>