Skip to content

Instantly share code, notes, and snippets.

@panesofglass
Created January 19, 2015 17:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save panesofglass/f1c0688157fe6a2ede8a to your computer and use it in GitHub Desktop.
Save panesofglass/f1c0688157fe6a2ede8a to your computer and use it in GitHub Desktop.
MVC with RxJS and D3
<div id="content"></div>

MVC with RxJS and D3

Prototype MVC (or MVI) client-side with RxJS and D3 to show use with various component libraries.

At present, RxJS Subjects are used like channels and not as part of an inheritance hierarchy.

A Pen by Ryan Riley on CodePen.

License.

// channels
var modelChanged = new Rx.Subject();
var buttonClicked = new Rx.Subject();
var addNewItem = new Rx.Subject();
var clearAll = new Rx.Subject();
// model proxy
function Model() {
var data = [];
var add =
addNewItem.subscribe(function (e) {
// Push to server-side resource
// Receive response from server-side resource
var newItem = 'new item';
data.push(newItem);
// Publish result
modelChanged.onNext(data);
});
var clear = clearAll.subscribe(function () {
data = [];
modelChanged.onNext(data);
});
return {
dispose: function () {
add.dispose();
clear.dispose();
}
};
}
// Controller maps UI events to model events
function Controller() {
var subscription = // return the disposable
buttonClicked.subscribe(function (toggle) {
switch (toggle) {
case 'Add':
addNewItem.onNext();
break;
case 'Clear':
clearAll.onNext();
break;
}
});
return subscription;
}
// view
function View(el) {
function init(el) {
var frag = document.createDocumentFragment();
var header = document.createElement('h1');
header.textContent = 'Demo';
var button = document.createElement('button');
button.textContent = 'Add';
var reset = document.createElement('button');
reset.textContent = 'Clear';
// Goal: delegate event listeners to component level
button.addEventListener('click', function (e) { buttonClicked.onNext('Add'); }, false);
reset.addEventListener('click', function (e) { buttonClicked.onNext('Clear'); }, false);
var ul = document.createElement('ul');
frag.appendChild(header);
frag.appendChild(button);
frag.appendChild(reset);
frag.appendChild(ul);
el.appendChild(frag);
}
function render(data) {
// NOTE: this can be greatly optimized
var ul = this.getElementsByTagName('ul')[0],
child;
// Clear
while (child = ul.firstChild) {
ul.removeChild(child);
}
// Redraw
data.map(function (item) {
var li = document.createElement('li');
li.textContent = item;
return li;
})
.forEach(function (li) {
ul.appendChild(li);
});
}
init(el);
var subscription = modelChanged.subscribe(render.bind(el));
// Return the disposable
return {
dispose: function () {
subscription.dispose();
// TODO: unbind any DOM event listeners and destroy DOM nodes
}
};
}
// Create model proxy
var model = Model();
// Create intent
var intent = Controller();
// Create view
var view = View(document.getElementById('content'));
// TODO: add D3 chart showing increase over time.
function dispose() {
model.dispose();
intent.dispose();
view.dispose();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment