简体   繁体   中英

Filter/update already rendered chart.js in react.js

I'm new here, because I have decided to dive into programming, so I can fill free time between treatments in the hospital. I'm absolutely new in the programming field with no previous coding background.

The summary:

I am working on a simple page, where I fetch data from a Postgre database that is visualized using chart.js. The page is a built-in cube.js playground, using a Reactjs template. Currently, I can display various charts depending on my criteria. Like display monthly sales of a certain product in Australia. Or, I can display a second chart with daily sales in the countries I choose. Or ignore all sales that were in a certain currency. Right now, every new criterion means I have to use cube.js playground and generate a new chart on the page. What I would like to achieve is to be able to filter already rendered charts (by a dropdown button outside the chart or inside the chart, it doesn't matter too much) and having the chart updated. Something like the pictures here , where the OP can filter charts based on the date, factory, etc.

I've tried Chart.js Example with Dynamic Dataset , chart.js tutorial on Updating Charts and various others. But I can't seem to be able to implement any of those solutions in my code.

Here is my current code:

ChartRenderer.js

import React from "react";
import PropTypes from "prop-types";
import { useCubeQuery } from "@cubejs-client/react";
import Row from "react-bootstrap/Row";
import Spin from "react-bootstrap/Spinner";
import Col from "react-bootstrap/Col";
import { Statistic, Table } from "antd";
import { Line, Bar, Pie } from "react-chartjs-2";
const COLORS_SERIES = [
    "#931F1D",
    "#141446",
    "#7A77FF",
];
const commonOptions = {
    maintainAspectRatio: true,
};
const TypeToChartComponent = {
    line: ({ resultSet }) => {
        const data = {
            labels: resultSet.categories().map((c) => c.category),
            datasets: resultSet.series().map((s, index) => ({
                label: s.title,
                data: s.series.map((r) => r.value),
                borderColor: COLORS_SERIES[index],
                backgroundColor: COLORS_SERIES[index],
                fill: false,
                tension: 0.4,
            })),
        };
        const options = { ...commonOptions };
        return <Line data={data} options={options} />;
    },
    bar: ({ resultSet }) => {
        const data = {
            labels: resultSet.categories().map((c) => c.category),
            datasets: resultSet.series().map((s, index) => ({
                label: s.title,
                data: s.series.map((r) => r.value),
                backgroundColor: COLORS_SERIES[index],
                fill: false,
            })),
        };
        const options = {
            ...commonOptions,
            scales: {
                xAxes: [
                    {
                        stacked: true,
                    },
                ],
            },
        };
        return <Bar data={data} options={options} />;
    },
    area: ({ resultSet }) => {
        const data = {
            labels: resultSet.categories().map((c) => c.category),
            datasets: resultSet.series().map((s, index) => ({
                label: s.title,
                data: s.series.map((r) => r.value),
                backgroundColor: COLORS_SERIES[index],
                fill: true,
            })),
        };
        const options = {
            ...commonOptions,
            scales: {
                yAxes: [
                    {
                        stacked: true,
                    },
                ],
            },
        };
        return <Line data={data} options={options} />;
    },
    pie: ({ resultSet }) => {
        const data = {
            labels: resultSet.categories().map((c) => c.category),
            datasets: resultSet.series().map((s) => ({
                label: s.title,
                data: s.series.map((r) => r.value),
                backgroundColor: COLORS_SERIES,
                hoverBackgroundColor: COLORS_SERIES,
                borderColor: COLORS_SERIES,
                hoverBorderColor: "white",
                hoverOffset: 10,
            })),
        };
        const options = { ...commonOptions };
        return <Pie data={data} options={options} />;
    },
    number: ({ resultSet }) => {
        return (
            <Row
                type="flex"
                justify="space-around"
                align="middle"
                style={{ height: "100%" }}
            >
                <Col align="left">
                    {resultSet.seriesNames().map((s) => (
                        <Statistic value={resultSet.totalRow()[s.key]} />
                    ))}
                </Col>
            </Row>
        );
    },
    table: ({ resultSet, pivotConfig }) => {
        return (
            <Table
                pagination={false}
                columns={resultSet.tableColumns(pivotConfig)}
                dataSource={resultSet.tablePivot(pivotConfig)}
            />
        );
    },
};


const TypeToMemoChartComponent = Object.keys(TypeToChartComponent)
    .map((key) => ({
        [key]: React.memo(TypeToChartComponent[key]),
    }))
    .reduce((a, b) => ({ ...a, ...b }));

const renderChart =
    (Component) =>
    ({ resultSet, error }) =>
        (resultSet && <Component resultSet={resultSet} />) ||
        (error && error.toString()) || <Spin animation="grow text-primary" />;

const ChartRenderer = ({ vizState }) => {
    const { query, chartType } = vizState;
    const component = TypeToMemoChartComponent[chartType];
    const renderProps = useCubeQuery(query);
    return component && renderChart(component)(renderProps);
};

ChartRenderer.propTypes = {
    vizState: PropTypes.object,
    cubejsApi: PropTypes.object,
};

ChartRenderer.defaultProps = {
    vizState: {},
    cubejsApi: null,
};

export default ChartRenderer;

DashBoardPage.js

import React from "react";
import Col from "react-bootstrap/Col";
import DateRangePicker from 'react-bootstrap-daterangepicker';
import ChartRenderer from "../components/ChartRenderer";
import Dashboard from "../components/Dashboard";
import DashboardItem from "../components/DashboardItem";

const DashboardItems = [
    {
        id: 0,
        name: "Sold by customers today",
        vizState: {
            query: {
                measures: ["PostgreSqlTable.amount"],
                timeDimensions: [
                    {
                        dimension: "PostgreSqlTable.added",
                        granularity: "day",
                        dateRange: "Today",
                    },
                ],
                order: {},
                dimensions: [],
                filters: [
                    {
                        member: "PostgreSqlTable.operation",
                        operator: "contains",
                        values: ["Sell"],
                    },
                ],
            },
            chartType: "number",
        },
    },
    {
        id: 1,
        name: "Bought by customers today",
        vizState: {
            query: {
                measures: ["PostgreSqlTable.amount"],
                timeDimensions: [
                    {
                        dimension: "PostgreSqlTable.added",
                        dateRange: "Today",
                    },
                ],
                order: {},
                filters: [
                    {
                        member: "PostgreSqlTable.operation",
                        operator: "contains",
                        values: ["Buy"],
                    },
                ],
            },
            chartType: "number",
        },
    },
    {
        id: 2,
        name: "Money in the wallet",
        vizState: {
            query: {
                measures: ["PostgreSqlTable.amount"],
                timeDimensions: [
                    {
                        dimension: "PostgreSqlTable.added",
                    },
                ],
                order: {
                    "PostgreSqlTable.amount": "desc",
                },
                dimensions: ["PostgreSqlTable.currency"],
                filters: [
                    {
                        member: "PostgreSqlTable.currency",
                        operator: "equals",
                        values: ["EUR"],
                    },
                ],
            },
            chartType: "number",
        },
    },
    {
        id: 3,
        name: "Monthly sales filtered by week",
        vizState: {
            query: {
                measures: ["PostgreSqlTable.amount"],
                timeDimensions: [
                    {
                        dimension: "PostgreSqlTable.added",
                        granularity: "week",
                        dateRange: "This month",
                    },
                ],
                order: {
                    "PostgreSqlTable.amount": "desc",
                },
                dimensions: ["PostgreSqlTable.operation"],
                filters: [
                    {
                        member: "PostgreSqlTable.operation",
                        operator: "notContains",
                        values: ["Register"],
                    },
                ],
                limit: 5000,
            },
            chartType: "line",
        },
    },
    {
        id: 4,
        name: "Countries with most customers",
        vizState: {
            query: {
                measures: ["PostgreSqlTable.count"],
                timeDimensions: [
                    {
                        dimension: "PostgreSqlTable.added",
                    },
                ],
                order: {
                    "PostgreSqlTable.count": "desc",
                },
                dimensions: ["PostgreSqlTable.country"],
                limit: 5,
            },
            chartType: "pie",
        },
    },
];


const DashboardPage = () => {
    const dashboardItem = (item) => (
        <Col className="col-4">
            <DashboardItem title={item.name}>
                <ChartRenderer vizState={item.vizState} />
                
            </DashboardItem>
        </Col>
    );

    const Empty = () => (
        <div
            style={{
                textAlign: "center",
                padding: 12,
            }}
        >
            <h2>
                No items added
            </h2>
        </div>
    );

    return DashboardItems.length ? (
        <Dashboard dashboardItems={DashboardItems}>
            {DashboardItems.map(dashboardItem)}
        </Dashboard>
    ) : (
        <Empty />
    );
};

export default DashboardPage;

At this moment, I have no clue how to implement the filter in react.js+chart.js. I have also tried to update the array, but no success ( I followed also this tutorial ) I would be most grateful for any help.

Thank you in advance, stay healthy. Tatsu

I'd recommend using the <QueryBuilder/> component available in the Cube.js-React integration ; this component provides a similar interface as that in the Developer Playground.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM