Support for Observable Arrays and Observable Objects is provided by jquery.observable.js.
Observable data is used by data linking and by JsViews in order to enable interactive data-driven views. JsViews integrates jQuery templates and data link, so that observable changes to data can automatically trigger updates in UI rendered by jQuery templates. Use of JsViews by the Grid can enable HTML rendered by the grid to update whenever the data is sorted, filtered, or mutated, without additional code. Where appropriate, the update can be incremental, without the performance hit of re-rendering the whole list of items.
Usually you just want to make one or more observable changes to an object or an array:
$.observable( myObject ).setField( "someField", value ); $.observable( myArray ).pop();
$.observable( data ) gives you as set of mutation methods for your data - based on whether it is an array or an object.
If you want, can hold on to it, as in
var myObservableArray = $.observable( myArray ); myObservableArray.pop(); myObservableArray.push( item ).shift();
If needed you can get your raw data item back:
myArray === myObservableArray.data(); // true
...But usually you won't bother. Just use $.observable( data ).foo() each time.
Supported mutation methods
Array
pop, push, reverse, shift, unshift, sort, splice. (Signatures identical to standard array methods)
move( fromIndex, numItems, toIndex)
replace( newItems )
Object
setField( path, value )
Note: path can be the field name, or can drill into child objects, as in setField( "address.city", "Seattle" );
Associated change events
Array
arrayChange
Note: associated eventArgs provides the method name and associated data or indexes, such as:
{ change: "add", newItems: [addedItem] }
Object
objectChange
Note: associated eventArgs provides the path and the value
{ path: "address.city", value: "Seattle" }
Extensibility
You can add methods for arrays or objects by extending $.observable.array or $.observable.object...
Example (This is how the replace method was added in jquery.observable.js):
$.observable.array.extend({ replace: function( newItems ) { this.splice.apply( this, [].concat( 0, this.data().length, newItems )); return this; } }
jQuery Templates Integration
The JsViews and JsRender implementation of jQuery templates allows the following:
// Get concatenated string obtained by applying the template to the data item (if object) or items (if array). var htmlString = $( "#myTemplate" ).render( myData ); // Insert the html into the DOM $( "#myContainer" ).html( htmlString ); // Activate the rendered HTML elements $( "#myContainer" ).dataLink( myData );
The effect of data linking (activation) is to add bindings to both the data and the rendered HTML, so that:
As a result, the following code will immediately trigger incremental insertion of a new rendered item under "#myContainer" above:
// Add item and trigger any rendered templates in the DOM to insert corresponding row $.observable( myData ).push( item );
Key changes to DataSource:
Listen to observable changes on input data (local data source):
datasource-local.js:
$.dataLink( this.options.input, function() { that.refresh(); });
Make observable changes when changing 'output' data:
datasource.js:
this.options.source( request, function( data, totalCount ) { $.observable( that ).setField( "totalCount", totalCount ); $.observable( that.options.data ).replace( data ); });
Key changes to Grid:
Grid depends only on data array, not on DataSource, and responds to observable changes on that data:
grid.js:
refresh: function() { ... tbody.html( $.render( template, source )) .dataLink( source ); ... }
http://borismoore.github.com/jquery-ui/grid-spf-observable/observable-previous/01_grid.html
The grids reflect the changes to the data on paging etc., as a result of observable collection events integrated with templates (without additional code).
Key code:
$( "#developers-data" ).grid({ selectMode: "single", columns: [ "firstName", "lastName", "country" ], source: developersInput }); function deleteSelectedDeveloper() { ... $.observable( developersInput ).splice( index, 1 ); developersDataGrid.select( prevIndex ); ... }
The grid reflects the changes, as a result of observable collection events.
Key code:
$( "#developers-data" ).grid({ selectMode: "single", columns: [ "firstName", "lastName", "country" ], source: developersInput, select: onSelectChange }); function onSelectChange( event, args ) { ... $( "#developerDetail" ) .html( $( "#detailViewTemplate" ).render( selectedItem.data ) ) .dataLink( selectedItem.data ); ... } <script id="detailViewTemplate" type="text/x-jquery-tmpl"> ... <input data-jq-linkfrom="firstName" data-jq-linkto="firstName" value="${firstName}"/> ... </script>
Full editing through Master-Detail UI. The grid reflects the changes, as a result of observable events.
http://borismoore.github.com/jquery-ui/grid-spf-observable/observable-previous/index.html
I've been collaborating with Boris to refine $.observable in ways that are important to Grid (and to jQuery generally). We'll post this work (including demos) to the wiki soon.
The aspects we're covering are:
-- Brad Olenick (brado@microsoft.com)