DEV Community

张一凡
张一凡

Posted on

Applying easy-model in Data Visualization Dashboards

Data visualization dashboards need to handle large amounts of data, chart configurations, user interactions, and other complex logic. easy-model's model-driven architecture can effectively organize these features, improving development efficiency. This article demonstrates easy-model's advantages in dashboard development through real examples.

Chart Configuration Model

Charts are the core of dashboards. We create ChartModel to encapsulate chart logic:

import { useModel } from "@e7w/easy-model"

class ChartModel {
  config = {
    type: "bar" as "bar" | "line" | "pie",
    data: [] as Array<{ label: string; value: number }>,
    title: "",
    showLegend: true
  };

  constructor(initialConfig: typeof this.config) {
    this.config = initialConfig;
  }

  updateData(newData: typeof this.config.data) {
    this.config.data = newData;
  }

  toggleLegend() {
    this.config.showLegend = !this.config.showLegend;
  }

  getMaxValue() {
    return Math.max(...this.config.data.map(d => d.value));
  }
}

function ChartComponent({ chartId }: { chartId: string }) {
  const chartModel = useModel(ChartModel, [{
    type: "bar",
    data: [
      { label: "Jan", value: 100 },
      { label: "Feb", value: 150 },
      { label: "Mar", value: 200 }
    ],
    title: "Monthly Sales",
    showLegend: true
  }]);

  return (
    <div>
      <h3>{chartModel.config.title}</h3>
      <p>Max Value: {chartModel.getMaxValue()}</p>
      <button onClick={() => chartModel.toggleLegend()}>
        {chartModel.config.showLegend ? "Hide Legend" : "Show Legend"}
      </button>
      {/* Chart rendering logic */}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

The model encapsulates chart business logic like data updates and configuration toggles.

Dashboard Layout Management

Dashboard layouts need to be shared across components:

import { provide, useInstance } from "@e7w/easy-model"

class DashboardModel {
  widgets: Array<{ id: string; type: string; position: { x: number; y: number } }> = [];
  dashboardId: string;

  constructor(dashboardId: string) {
    this.dashboardId = dashboardId;
  }

  addWidget(widget: typeof this.widgets[0]) {
    this.widgets.push(widget);
  }

  moveWidget(id: string, newPosition: { x: number; y: number }) {
    const widget = this.widgets.find(w => w.id === id);
    if (widget) widget.position = newPosition;
  }

  getWidgetCount() {
    return this.widgets.length;
  }
}

const DashboardProvider = provide(DashboardModel);

function Dashboard({ dashboardId }: { dashboardId: string }) {
  const dashboard = useInstance(DashboardProvider(dashboardId));

  return (
    <div>
      <h2>Dashboard ({dashboard.getWidgetCount()} widgets)</h2>
      {dashboard.widgets.map(widget => (
        <div key={widget.id} style={{
          position: 'absolute',
          left: widget.position.x,
          top: widget.position.y
        }}>
          {/* Render widget */}
        </div>
      ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

provide supports shared layout state.

Asynchronous Data Loading

Dashboard data typically comes from APIs:

import { loader, useLoader, useModel } from "@e7w/easy-model"

class DataSourceModel {
  data: any[] = [];
  sourceId: string;

  constructor(sourceId: string) {
    this.sourceId = sourceId;
  }

  @loader.load(true)
  async loadData() {
    // Simulate API call
    const response = await fetch(`/api/data/${sourceId}`);
    this.data = await response.json();
  }
}

function DataWidget({ sourceId }: { sourceId: string }) {
  const dataModel = useModel(DataSourceModel, [sourceId]);
  const { isLoading } = useLoader();

  return (
    <div>
      {isLoading(dataModel.loadData) ? "Loading..." : (
        <ul>
          {dataModel.data.map((item, i) => <li key={i}>{item.name}</li>)}
        </ul>
      )}
      <button onClick={() => dataModel.loadData()} disabled={isLoading(dataModel.loadData)}>
        Refresh Data
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Asynchronous data loading with automatic state management.

Testing Ensures Quality

Model classes are easy to test:

describe("ChartModel", () => {
  it("should update data", () => {
    const model = new ChartModel({
      type: "bar",
      data: [],
      title: "Test",
      showLegend: true,
    });
    const newData = [{ label: "A", value: 10 }];
    model.updateData(newData);
    expect(model.config.data).toEqual(newData);
  });

  it("should calculate max value", () => {
    const model = new ChartModel({
      type: "bar",
      data: [
        { label: "A", value: 10 },
        { label: "B", value: 20 },
      ],
      title: "Test",
      showLegend: true,
    });
    expect(model.getMaxValue()).toBe(20);
  });
});
Enter fullscreen mode Exit fullscreen mode

Tests ensure chart logic correctness.

Conclusion

easy-model performs excellently in data visualization dashboards: model encapsulation of chart logic, shared layout state, async data handling. Combined with testing, improves development quality. Try easy-model for simpler dashboard development!

Project repo: GitHub

Top comments (0)