Skip to main content
Version: 1.0.0

Understanding DataModel Propagation

DataModel propagation enables interactive data synchronization across multiple UI components. When data changes in one component, these changes can be automatically reflected in other related components.

How Propagation Works

Propagation in DataModel follows these principles:

  1. A root DataModel instance can propagate changes to its derived DataModels
  2. Changes are propagated through a selection query that identifies affected rows
  3. Each derived DataModel receives the filtered data and can respond accordingly
  4. UI components connected to these DataModels can update to reflect the changes
info

Propagation is particularly useful for creating interactive dashboards where filtering or selecting data in one view should update other related views.

Basic Concepts

Selection Query

A criteria object that defines which data should be affected:

{
criteria: {
field: "Origin",
value: "USA",
operator: "eq"
},
fields: ["Origin"]
}

Propagation Handler

A callback function that receives the filtered DataModel and handles the UI update:

dataModel.onPropagation((filteredDm, info) => {
// Update UI with filtered data
});

Tutorial: Creating Connected Data Grids

Let's build an example with two connected grids that respond to the same filter operations.

note

Remember that the Grid component used in these examples is just for demonstration. The propagation concepts can be applied with any UI components that can render DataModel data. Most commonly, you will be using Muze for your data visualization needs in conjunction with DataModel.

Step 1: Create the Base DataModel

First, let's create our root DataModel:

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

Step 2: Create Derived DataModels

Create two different groupings of the data:

// Group by Origin
let groupedOrigin = rootDm.groupBy(["Origin"]);

// Group by Maker
let groupedMaker = rootDm.groupBy(["Maker"]);

The grouped data will look like this:

Grouped by Origin:

OriginMiles_per_GallonDisplacementHorsepowerWeight_in_lbsAcceleration
USA20.13455119.61180014.93
Europe27.8918381182516.82
Japan30.4516879.84161316.17

Grouped by Maker:

MakerMiles_per_GallonDisplacementHorsepowerWeight_in_lbsAcceleration
chevrolet20.47454114.11203515.23
buick19.18455136.41215514.70
plymouth21.70440113.41187514.72

Step 3: Set Up the UI

Create the HTML structure for our grids:

<div class="app-container d-flex flex-column">
<div id="input-toolbar"></div>
<div>
<div class="grid-1-sort-input"></div>
<div id="grid-1" class="grid"></div>
</div>
<div>
<div class="grid-2-sort-input"></div>
<div id="grid-2" class="grid"></div>
</div>
</div>

Step 4: Initialize the Grids

const g1 = new Grid("#grid-1");
const g2 = new Grid("#grid-2");

// Initial render
g1.render(groupedOrigin);
g2.render(groupedMaker);

Step 5: Set Up Propagation Handlers

Add handlers to respond to data changes:

// Handle updates for Origin grid
groupedOrigin.onPropagation((filteredDm, info) => {
g1.render(filteredDm);
});

// Handle updates for Maker grid
groupedMaker.onPropagation((filteredDm, info) => {
g2.render(filteredDm);
});

Step 6: Connect Filter Controls

Set up the filter toolbar and connect it to propagation:

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

// Add filter handler
addFilterEventListener(({ field, value, operator }) => {
rootDm.propagate(
[{
criteria: {
field: field,
value: value,
operator: Datamodel.ComparisonOperators[operator]
},
fields: [field]
}],
{
payload: { action: "filter" }
}
);
});

// Add reset handler
addResetEventListener(() => {
g1.render(groupedOrigin);
g2.render(groupedMaker);
});

Complete Implementation

const Datamodel = muze.DataModel;

// Create base DataModel
const formattedData = await Datamodel.loadData(data, schema);
let rootDm = new Datamodel(formattedData);

// Create grouped DataModels
let groupedOrigin = rootDm.groupBy(["Origin"]);
let groupedMaker = rootDm.groupBy(["Maker"]);

// Initialize grids
const g1 = new Grid("#grid-1");
const g2 = new Grid("#grid-2");

// Initial render
g1.render(groupedOrigin);
g2.render(groupedMaker);

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

// Set up propagation handlers
groupedOrigin.onPropagation((filteredDm, info) => {
g1.render(filteredDm);
});

groupedMaker.onPropagation((filteredDm, info) => {
g2.render(filteredDm);
});

// Add filter handler
addFilterEventListener(({ field, value, operator }) => {
rootDm.propagate(
[{
criteria: {
field: field,
value: value,
operator: Datamodel.ComparisonOperators[operator]
},
fields: [field]
}],
{
payload: { action: "filter" }
}
);
});

// Add reset handler
addResetEventListener(() => {
g1.render(groupedOrigin);
g2.render(groupedMaker);
});

Example Results

tip

Propagation is particularly powerful when:

  • Building interactive dashboards
  • Creating linked visualizations
  • Implementing cross-filtering functionality
  • Maintaining data consistency across multiple views