Skip to main content
Version: Current

Connecting DataModel to UI Components

This guide demonstrates how to connect a DataModel instance to UI components, using a sample Grid implementation as an example. The concepts shown here can be applied to any UI component that needs to display and interact with DataModel data.

note

The Grid component used in these examples is a simple utility class for demonstration purposes. It's not part of DataModel. Your visualization library of choice is usually going to be Muze which can accept DataModel instances and render visualizations out of them.

Understanding the Example Components

Before we dive into the implementation, let's understand the utility components we'll be using for demonstration:

Grid Component

A simple utility class that renders DataModel data as an HTML table:

  • Accepts a DOM selector for mounting
  • Has a render() method that takes a DataModel instance
  • Provides methods for adding sort inputs and handling sort events

Toolbar Component

A utility component that provides UI controls for DataModel operations:

  • Created using initToolbar(selector, schema)
  • Provides inputs for grouping, filtering, and resetting
  • Exposes event listeners for each operation type

Event Listeners

The toolbar provides several event listeners for handling DataModel operations:

  1. addGroupByEventListener(callback)

    • Handles grouping operations
    • Callback receives { field } parameter
    • field is the column name to group by
  2. addFilterEventListener(callback)

    • Handles filtering operations
    • Callback receives { field, value, operator } parameters
    • operator corresponds to DataModel.ComparisonOperators
  3. addResetEventListener(callback)

    • Handles reset operations
    • Callback receives no parameters
    • Used to restore original DataModel state

Basic Setup

First, let's create our DataModel instance from sample data:

async function main() {
const DataModel = muze.DataModel;
const formattedData = await DataModel.loadData(data, schema);
const dm = new DataModel(formattedData);
}

Step 1: Basic Grid Rendering

Let's start with the simplest case - rendering DataModel data in a grid:

const grid = new Grid("#grid");
grid.render(dm);

This will display your data as a simple HTML table:

NameMakerMiles_per_GallonDisplacementHorsepowerWeight_in_lbsAccelerationOriginCylindersYear
chevrolet chevelle malibuchevrolet18307130350412USA81970-0-1
buick skylark 320buick15350165369311.50USA81970-0-1

Step 2: Adding Group By Operations

Let's implement a simple grouping operation first. We'll create a button that groups data by origin:

const Datamodel = muze.DataModel;
const formattedData = await Datamodel.loadData(data, schema);
let dm = new Datamodel(formattedData);

// Initialize grid
const grid = new Grid("#grid-1");
grid.render(dm);

// Add grouping button
const btn = document.createElement("button");
btn.innerText = "Group by Origin";
btn.className = "btn btn-light";
btn.addEventListener("click", () => {
const newDm = dm.groupBy(["Origin"]);
grid.render(newDm);
});

Step 3: Setting Up the Toolbar

Now let's replace our simple button with a more comprehensive toolbar:

// Initialize the toolbar with our schema
initToolbar("#input-toolbar", dm.getData().schema);

// Add group by handler
addGroupByEventListener(({ field }) => {
const outputDm = dm.groupBy([field]);
grid.render(outputDm);
});

Step 4: Adding Filtering Capabilities

The toolbar also provides filtering capabilities. Let's add a filter handler:

addFilterEventListener(({ field, value, operator }) => {
// Create a new filtered DataModel instance
const outputDm = dm.select({
field,
value,
operator: Datamodel.ComparisonOperators[operator],
});
// Update the grid with filtered data
grid.render(outputDm);
});

Step 5: Adding Sorting Capabilities

The Grid component provides built-in sort functionality:

const grid = new Grid("#grid-1")
// Add sort input UI
.addSortInput("#grid-1-sort-input", dm.getSchema())
// Handle sort events
.onSort((dm, { field, order }) => {
const newDm = dm.sort([[field, order]]);
return newDm;
});

Step 6: Adding Reset Functionality

Finally, let's add the ability to reset to the original data:

addResetEventListener(() => {
grid.render(dm);
});

Complete Implementation

Now that we understand each component, here's how they all work together:

<div class="app-container d-flex flex-column">
<div id="input-toolbar"></div>
<div>
<div id="grid-1-sort-input"></div>
<div id="grid-1" class="grid"></div>
</div>
</div>
const Datamodel = muze.DataModel;
const formattedData = await Datamodel.loadData(data, schema);
let dm = new Datamodel(formattedData);

// Initialize grid with sorting capability
const grid = new Grid("#grid-1")
.addSortInput("#grid-1-sort-input", dm.getSchema())
.onSort((dm, { field, order }) => {
const newDm = dm.sort([[field, order]]);
return newDm;
});

// Initial render
grid.render(dm);

// Initialize toolbar
initToolbar("#input-toolbar", dm.getData().schema);

// Add all event handlers
addGroupByEventListener(({ field }) => {
const outputDm = dm.groupBy([field]);
grid.render(outputDm);
});

addFilterEventListener(({ field, value, operator }) => {
const outputDm = dm.select({
field,
value,
operator: Datamodel.ComparisonOperators[operator],
});
grid.render(outputDm);
});

addResetEventListener(() => {
grid.render(dm);
});
tip

This example demonstrates a common pattern when working with DataModel:

  1. Create a new DataModel instance for each operation
  2. Update the UI with the new instance
  3. Keep the original instance for reset functionality

Example Output

The complete implementation provides:

  • A toolbar for grouping and filtering operations
  • Sort controls in the grid header
  • A reset button to restore the original view
  • Real-time updates as operations are performed
note

Remember that the Grid and toolbar components used here are for demonstration only. The key concepts - creating new DataModel instances for each operation and updating the UI accordingly - can be applied with any UI framework or library.