Thursday, June 2, 2011

Javascript databindings with observers

This is not quite as clear as the previous post, but heres a version of my javascrip prototyping with observers on the data. Again you can play with it using jsfiddle .. or heres the code.
<h1>Test for binding data into dynamic forms using javascript closures</h1>
This creates multiple divs (with each click on the add button) and binds the contained control to
an element of the <code>data</code> array.
<br/><a id="add_button" href="#">Click me to add a new datum</a><br/>
<div id="target_div"></div>
<div id="data"></div>
view raw fiddle.html hosted with ❤ by GitHub
$(document).ready( function() {
$('#add_button').click( add_pane_with_data_binding );
} );
var data = [];
var DatumClass = function(v) { this._v = v; this._watchers=[]; }
DatumClass.prototype = new Object;
DatumClass.prototype.get_value = function() { return this._v; }
DatumClass.prototype.set_value = function(v,token)
{
this._v = v;
//Notify the watchers
var len=this._watchers.length
for( var i=0; i<len; ++i )
{
console.log(this._watchers[i]);
this._watchers[i].value_changed(this, this._v, token);
}
}
var ViewClass = function(input,datum) { this._input = input; this._datum = datum; }
ViewClass.prototype = new Object;
ViewClass.prototype.value_changed = function( datum, value, token )
{
if( token!=this._input && this._input.val()!=value ) this._input.val(value);
}
function create_bound_input( datum )
{
var this_input = $('<input type="text"></input>');
this_input.val( datum.get_value() );
//Now bind the change via a closure
this_input.bind('change', function(e) { datum.set_value( this_input.val(), this_input ); } );
//Now add them as watchers so that if someone else changes the value they get notified
var observer = new ViewClass( this_input, datum );
datum._watchers.push(observer);
return this_input;
}
//Lets keep our data display uptodate automatically as a watcher too..
var json_watcher={};
json_watcher.value_changed = function( datum, value, token)
{
$('#data').empty();
var len = data.length;
for( var i=0; i<len; ++i )
{
var vv = $( '<span>'+data[i].get_value()+'</span>' );
vv.css({"font-family":"sans-serif", "border":"1px solid #cc0"});
$('#data').append($('<div><b>'+i+'</b></div>').append(vv));
}
}
function add_pane_with_data_binding()
{
//Add the data
var this_data = new DatumClass("Hello World");
data.push( this_data );
//Add view to the target div
var this_div = $('<div></div>');
$('#target_div').append( this_div );
var this_input = create_bound_input(this_data);
this_div.append( this_input );
var this_input2 = create_bound_input(this_data);
this_div.append( this_input2 );
//Lets keep our data display uptodate automatically as a watcher too..
this_data._watchers.push(json_watcher);
//And manually update the value now anyway.
json_watcher.value_changed();
}
view raw fiddle.js hosted with ❤ by GitHub

Creating data bindings using javascript closures

I've been working on javascript codebase that stores all its data in forms, hiding and showing all the form elements as the view changes. This has worked well while the structure of the data was relatively rigid. But now we've got dynamically structured (tree-like) data we need to be able to grow and shrink the views in more flexible ways.

While I'm not a gung-ho MVC advocate, it certainly looked like the app could do with a bit of an MVC style cleanup (the data was in the view rather than being accessed by the view).

The problem was it wasn't clear how to get the data out of the view... here's a simplified version of the method I'm planning to use.

<h1>Test for binding data into dynamic forms using javascript closures</h1>
This creates multiple divs (with each click on the add button) and binds the contained control to
an element of the <code>data</code> array.
<br/><a id="add_button" href="#">Click me to add a new datum</a><br/>
<div id="target_div"></div>
<a id="show_data" href="#">Show Data</a>
<div id="data"></div>
view raw fiddle.html hosted with ❤ by GitHub
$(document).ready( function() {
$('#add_button').click( add_pane_with_data_binding );
$('#show_data').click( show_data );
} );
var data = [];
function add_pane_with_data_binding()
{
//Add the data
var this_data = { "v":"Hello World" };
data.push( this_data );
//Add view to the target div
var this_div = $('<div></div>');
var this_input = $('<input type="text"></input>');
this_div.append( this_input );
$('#target_div').append( this_div );
this_input.val( this_data.v );
//Now bind the change via a closure
function on_change_function(e)
{
this_data.v = this_input.val();
}
this_input.bind('change',on_change_function);
}
function show_data()
{
$('#data').html( '<pre>'+JSON.stringify(data,null,2)+'</pre>' );
}
view raw fiddle.js hosted with ❤ by GitHub

You can also play with this using jsfiddle

I'm thinking about implementing broadcasting of changes and more model based approach later... but maybe that'll have to be another post.