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.
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:
-
addGroupByEventListener(callback)
- Handles grouping operations
- Callback receives
{ field }
parameter field
is the column name to group by
-
addFilterEventListener(callback)
- Handles filtering operations
- Callback receives
{ field, value, operator }
parameters operator
corresponds to DataModel.ComparisonOperators
-
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:
Name | Maker | Miles_per_Gallon | Displacement | Horsepower | Weight_in_lbs | Acceleration | Origin | Cylinders | Year |
---|---|---|---|---|---|---|---|---|---|
chevrolet chevelle malibu | chevrolet | 18 | 307 | 130 | 3504 | 12 | USA | 8 | 1970-0-1 |
buick skylark 320 | buick | 15 | 350 | 165 | 3693 | 11.50 | USA | 8 | 1970-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);
});
This example demonstrates a common pattern when working with DataModel:
- Create a new DataModel instance for each operation
- Update the UI with the new instance
- 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
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.