SkillAgentSearch skills...

Model

A functional reactive model library for interactive data visualization.

Install / Use

/learn @curran/Model
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

model.js

Build Status

model.js is available on npm. To install it, type:

npm install model-js --save

A functional reactive model library - Model.js manages the execution flow of the data flow graphs you define. Kind of like Backbone and React, but simpler and designed specifically for making D3 easier to use. Also check out Chiasm, a visualization runtime engine built on Model.js.

Installable via Bower: bower install model.

Installable via JSPM: jspm install model=github:curran/model.

Usable as:

  • an AMD (RequireJS) module define(["model"], function(Model){ ... });
  • a CommonJS (Node) module var Model = require("model");
  • a browser global <script src="model.js"></script>

Also, check out this redesign of this library - reactive-model.

Public API

var model = Model([defaults]);

  • The model constructor function.
  • Using "new" is optional.
  • The optional defaults constructor argument is an object with default property values.
  • The returned model object can be treated as a plain JavaScript Object for setting and getting property values, e.g.
    • model.x = 5;
    • console.log(model.x);

var listener = model.when(properties, callback [, thisArg]);

  • Listens for changes to the given dependency properties.
  • properties Either an array of strings or a string. Specifies the dependency properties.
  • callback A callback function that is called:
    • with dependency property values as arguments,
    • only if all dependency properties have values,
    • once for initialization,
    • whenever one or more dependency properties change,
    • on the next tick of the JavaScript event loop after dependency properties change,
    • only once as a result of one or more changes to dependency properties.
  • thisArg An optional argument bound to this in the callback.
  • Returns a listener object that can be used to remove the callback.

model.cancel(listener)

  • Removes the listener returned by when. This means the callback will no longer be called when properties change.

model.on(property, callback(newValue, oldValue)[, thisArg])

  • Adds a change listener for the given property.
  • Kind of like on in Backbone

model.off(property, callback)

  • Removes a change listener for the given property.
  • Kind of like off in Backbone

model.set(values)

  • A convenience function for setting many model properties at once.
  • Assigns each property from the given values object to the model.
  • This function can be used to deserialize models, e.g.:
    • var json = JSON.stringify(model);
    • ... later on ..
    • model.set(JSON.parse(json));

Data Dependency Graphs

Setting model properties in when callbacks enables creating reactive data dependency graphs. . As a simple example, consider a fullName property that is computed from firstName and lastName.

model.when(['firstName', 'lastName'], function (firstName, lastName) {
  model.fullName = firstName + ' ' + lastName;
});

Here's a full working example that computes fullName and uses HTML forms.

<img src="http://curran.github.io/model/images/computedProperty.png">

The following example demonstrates construction of a data dependency graph in which the flow propagates two hops from x to y to z.

model.when('x', function (x) {
  model.y = x + 1;
});
model.when('y', function (y) {
  model.z = y * 2;
});
<img src="http://curran.github.io/model/images/dependencyGraph.png">

This pattern can be used to build up reactive data dependency graphs of arbitrary complexity.

Reactive Visualizations

As an example of how data dependency graphs can be used for creating visualizations, consider this bar chart.

Bar Chart

This is the reactive data flow graph of the bar chart.

Bar Chart Flow

The diagram was constructed using the reactive flow diagram renderer. Lambdas represent reactive functions, and labeled nodes represent model properties.

Though the entire reactive flow may seem complex, programming it is simple, because each reactive function is defined independently, only knowing about its direct dependencies and the properties that it changes. This is why functional reactive programming is so great.

Multiple reactive visualizations can be combined together to form visualization dashboards with multiple linked views. For example, take a look at the linked views example, which looks like this:

<img src="http://curran.github.io/model/images/linkedViews.png"> Brushing in the scatter plot causes the selected data to be aggregated and plotted in the bar chart ([run it!](http://curran.github.io/model/examples/d3LinkedViews/)).

See Also

Motivation

This library was created in order to cleanly define reactive model-driven data visualizations. When using Backbone and Underscore to define model-driven visualizations, there is a pattern that appears again and again for executing code that depends on multiple model properties. For example, consider a Backbone model that has a size property that contains the width and height of the visualization, and a data property that contains the array of data to be visualized. This is the code you want to write:

model.on('change:width change:height change:data', function (){
  var width = model.get('width'),
      height = model.get('height'),
      data = model.get('data');
  // Build the visualization using width, height and data.
});

However, with the above code, if only one or two of the properties are set, the function will be invoked before all properties are defined. Therefore a null check must be added for all properties as follows:

model.on('change:width change:height change:data', function (){
  var width = model.get('width'),
      height = model.get('height'),
      data = model.get('data');
  if(width && height && data) {
    // Build the visualization using width, height and data.
  }
});

The above code now does not break, but has another issue. When width and height are both updated, the function is invoked twice. Ideally, when width and height are updated in sequence (e.g. `model.set('width', 50); model.set('h

Related Skills

View on GitHub
GitHub Stars313
CategoryDevelopment
Updated6mo ago
Forks41

Languages

JavaScript

Security Score

87/100

Audited on Sep 17, 2025

No findings