Skip to content
Permalink
Browse files

Overhaul $.observable binding, add $.observable._triggerProperty to d…

…eal with change events on arrays.
  • Loading branch information
jzaefferer committed Sep 15, 2011
1 parent 6dd6814 commit 174289fe0dbdb49375c54f3e40af560b7afaae92
@@ -20,17 +20,19 @@ $.widget( "ui.gridEditor", {
},
_create: function() {
var grid = this.element.data("grid");

this._bind({
dblclick: function( event ) {
var target = $( event.target ).closest( this.options.items );
var parent = this.options.parentInput;
if ( target.length && !target.data( "editor" ) ) {
target.editor({
editor: this.options.editor( target, grid ),
editorOptions: this.options.editorOptions( target, grid ),
submit: function( event, ui) {
var object = target.closest("tr").data( "grid-item" ),
property = grid.options.columns[ target[ 0 ].cellIndex ].property;
$.observable( object ).property( property, ui.value );
$.observable( object, parent ).property( property, ui.value );
}
}).editor("start");
}
@@ -23,6 +23,7 @@
<script src="navigator.js"></script>
<script src="localstore.js"></script>
<script src="helpers.js"></script>
<script src="observable.js"></script>
<script>
var store = $.demos.localstore( {
key: "grid-editing-developers",
@@ -43,7 +44,7 @@
});
var grid = $.ui.grid( {
source: developers
source: developers.toArray()
}, "#developers-local" );
// TODO refactor this - there must be a better way to reuse the editor
@@ -61,22 +61,8 @@
$(".selected-status").text(selected.length);
});
// TODO $.observable should support this binding, along with a less intrusive event facility
function bindChange(index, developer) {
$.observable( developer ).bind("change", function(event, ui) {
developers.refresh().save();
});
}
$.observable( localDevelopers ).bind("insert remove", function(event, ui) {
if ( event.type === "insert" ) {
$.each( ui.items, bindChange );
}
developers.refresh().save();
})
$.each( localDevelopers, bindChange );
var grid = $( "#developers-local" ).grid({
source: developers
source: developers.toArray()
});
grid.bind("gridrefresh", function() {
@@ -145,7 +131,8 @@
});
var editForm = $( "#editForm" ).tooltip().hide().submit( function( event ) {
event.preventDefault();
$.observable(developer).property( serializeForm( this ) );
$.observable( developer, localDevelopers ).property( serializeForm( this ) );
// TODO hide tooltip
editForm.hide();
});
@@ -52,30 +52,14 @@
limit: 8
}
});
developers.save = function() {
store.save( localDevelopers );
return this;
}
// TODO $.observable should support this binding, along with a less intrusive event facility
function bindChange(index, developer) {
$.observable( developer ).bind("change", function(event, ui) {
developers.refresh().save();
});
}
$.observable( localDevelopers ).bind("insert remove", function(event, ui) {
if ( event.type === "insert" ) {
$.each( ui.items, bindChange );
}
developers.refresh().save();
})
$.each( localDevelopers, bindChange );
var grid = $( "#developers-local" ).grid({
source: developers
source: developers.toArray()
});
grid.gridEditor({
items: "td:not(:has(button))",
parentInput: localDevelopers,
done: function() {
// for the navigator
grid.focus();
@@ -86,7 +70,8 @@
var developer;
var editForm = $( "#editForm" ).tooltip().hide().submit( function( event ) {
event.preventDefault();
$.observable(developer).property( serializeForm( this ) );
$.observable( developer, localDevelopers ).property( serializeForm( this ) );
// TODO make the tooltip disappear when hiding the form
editForm.hide();
});
grid.delegate( "button.edit", "click", function() {
@@ -54,14 +54,23 @@ $.widget( "demos.localstore", {
stored = this.data;
}
if (!stored) {
return this.options.intial;
return this.bind(this.options.intial);
}
return stored;
return this.bind(stored);
},
save: function( data ) {
localStorageSupport
? localStorage.setItem( this.options.key, JSON.stringify( data ) )
: this.data = data;
},
bind: function( data ) {
if (!data)
return;
var that = this;
$.observable( data ).bind( "insert remove refresh change", function() {
that.save(this);
});
return data;
}
});

@@ -23,18 +23,7 @@
var movies = store.load();
$(function() {
// TODO $.observable should support this binding, along with a less intrusive event facility
function bindChange(index, movie) {
$( [ movie ] ).bind("change", function() {
store.save( movies );
$(store).trigger("update");
});
}
$.each(movies, bindChange);
$( [ movies ] ).bind("insert remove refresh", function(event, ui) {
if ( event.type === "insert" ) {
$.each( ui.items, bindChange );
}
$( [ movies ] ).bind("insert remove refresh change", function(event, ui) {
store.save( movies );
$(store).trigger("update");
});
@@ -44,7 +33,7 @@
var movie;
var editForm = $( "#editForm" ).hide().submit( function( event ) {
event.preventDefault();
$.observable( movie ).property( serializeForm( this ) );
$.observable( movie, movies ).property( serializeForm( this ) );
editForm.hide();
});
var list = $("#movies-list").delegate("li:has(li)", "click", function() {
@@ -3,14 +3,15 @@
*
*/
(function ( $, undefined ) {
$.observable = function( data ) {
return new observable( data );
$.observable = function( data, parent ) {
return new observable( data, parent );
};

var splice = [].splice;

function observable( data ) {
function observable( data, parent ) {
this.data = data;
this.parent = parent;
}
observable.prototype = {
data: null,
@@ -49,7 +50,7 @@
}
}
if ( changed ) {
this._trigger( "change", {
this._propertyTrigger( "change", {
oldValues: oldValues,
newValues: newValues
});
@@ -65,7 +66,7 @@
oldValues[ path ] = oldValue;
var newValues = {};
newValues[ path ] = value;
this._trigger( "change", {
this._propertyTrigger( "change", {
oldValues: oldValues,
newValues: newValues
});
@@ -75,6 +76,15 @@
return this;
},

_propertyTrigger: function( type, data ) {
this._trigger( type, data );
if ( this.parent ) {
$([ this.parent ]).triggerHandler( type, $.extend({
item: this.data
}, data) );
}
},

insert: function( index, items) {
// insert( object )
if ( $.type(index) === "object" ) {
@@ -38,6 +38,10 @@
response( result, data.totalResultsCount );
}
});
},
// could also include $.observable to get the same effect
response: function() {
grid.grid('refresh');
}
});
@@ -51,8 +55,8 @@
});
// secondary view
$( "table" ).grid({
source: cities,
var grid = $( "table" ).grid({
source: cities.toArray(),
columns: [ "name", "adminName1", "adminCode1", "fclName", "adminName2", "population", "countryName" ]
});
});
@@ -9,6 +9,16 @@ $.widget( "ui.localDatasource", $.ui.datasource, {
this.options.source = function( request, response) {
var sortedItems = that._sort( that._filter( that.options.input ) );
response( that._page( sortedItems ), sortedItems.length );
};
if ( $.observable ) {
$.observable( this.options.input ).bind( "insert remove refresh change", function(event, ui) {
// forward change event, otherwise do full refresh
if ( event.type === "change" ) {
$.observable( that.data )._trigger( "change", ui );
return;
}
that.refresh();
});
}
},
_filter: function( items ) {
@@ -24,11 +34,10 @@ $.widget( "ui.localDatasource", $.ui.datasource, {
}
return match;
});
} else {
// copy input array to avoid sorting original
// TODO need this only when actually sorting, not for paging, which slices anyway
return $.makeArray( items );
}
}
// copy input array to avoid sorting original
// TODO need this only when actually sorting, not for paging, which slices anyway
return $.makeArray( items );
},
_match: function( value, filter ) {
var operator = filter.operator || "==",
@@ -40,7 +49,7 @@ $.widget( "ui.localDatasource", $.ui.datasource, {
case "<=": return value <= operand;
case ">": return value > operand;
case ">=": return value >= operand;
case "like": return new RegExp( operand.replace( /[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&" ), "i" ).test( value )
case "like": return new RegExp( operand.replace( /[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&" ), "i" ).test( value );
default: throw "Unrecognized filter operator: " + operator + " for operand " + operand;
}
},
@@ -53,22 +62,20 @@ $.widget( "ui.localDatasource", $.ui.datasource, {
value2 = item2[ property ];
if ( value1 == value2 ) {
if ( secondary.length ) {
var next = secondary[ 0 ]
var next = secondary[ 0 ];
return sorter( next, secondary.slice( 1 ) )( item1, item2 );
} else {
return 0;
}
return 0;
}
return order * ( value1 > value2 ? 1 : -1 );
}
};
}
if ( this.options.sort.length ) {
var sorts = this.options.sort;
var first = sorts[ 0 ];
return items.sort( sorter( first, sorts.slice( 1 ) ) );
} else {
return items;
}
}
return items;
},
_page: function( items ) {
var paging = this.options.paging,
@@ -11,6 +11,13 @@ $.widget( "ui.datasource", {
filter: null
},

// TODO subwidgets override _create, should we force them to call _super("_create")?
// or is there a way to have a constructor along with _create?
// _init is probably safe here, as this shouldn't get called as a widget anyway
_init: function() {
this.data = [];
},

toArray: function() {
return this.data;
},
@@ -64,9 +71,12 @@ $.widget( "ui.datasource", {
page: this.page()
});
var that = this;
this.options.source( request, function( data, totalCount ) {
that.data = data;
this.options.source( request, function( data, totalCount ) {
Array.prototype.splice.apply( that.data, [ 0, that.data.length ].concat( data ) );
that.totalCount = parseInt(totalCount, 10);
// $.observable
$([ that.data ]).triggerHandler( "refresh" );
// old school
that._trigger( "response" );
});
return this;
@@ -15,6 +15,7 @@
<script src="datasource-odata.js"></script>
<script src="grid.js"></script>
<script src="pager.js"></script>
<script src="../grid-editing/observable.js"></script>
<script>
var localDevelopers;
$.ajax({
@@ -86,7 +87,7 @@
$( "#developers-local" ).grid({
columns: [ "firstName", "lastName", "country" ],
source: developers,
source: developers.toArray(),
select: function( event, ui ) {
console.log( "Selected " + ui.item.firstName );
}
@@ -105,7 +106,7 @@
resource: "http://odata.netflix.com/Catalog/Titles"
});
$( "#movies" ).grid({
source: movies
source: movies.toArray()
});
$( "#filterMovies" ).filterControl( movies );

0 comments on commit 174289f

Please sign in to comment.
You can’t perform that action at this time.