Skip to content

Commit

Permalink
Finalizing implementation of the template binding and adding supporti…
Browse files Browse the repository at this point in the history
…ng docs.
  • Loading branch information
gmac committed Mar 22, 2013
1 parent 2bf4b46 commit 0d73ff0
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 73 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ For all complete information and documentation, visit the project's website at [

## Change Log

**0.11.0***March 22, 2013*[Diff](https://github.com/gmac/backbone.epoxy/compare/v0.10.1...v0.11.0 "Diff: v0.10.1/v0.11.0")

Adds final features planned for 1.0 release.

- Adds `template` binding for rendering data attributes with an Underscore template.
- Defines `binding` API, intended for library add-on extensions.
- Revised docs.

**0.10.1***March 21, 2013*[Diff](https://github.com/gmac/backbone.epoxy/compare/v0.10.0...v0.10.1 "Diff: v0.10.0/v0.10.1")

Large codebase refactoring.
Expand All @@ -38,7 +46,7 @@ Large codebase refactoring.

Adds additional binding features to stage for a 1.0 release.

- Adds select menu options binding suite and supporting tests; includes:
- Adds select menu `options` binding suite and supporting tests; includes:
- `options` : Binds the contents of an Array or Collection to select menu options.
- `optionsDefault` : Provides a default option to include above `options` items.
- `optionsEmpty` : Provides an empty placeholder option to display when `options` is empty.
Expand Down
16 changes: 7 additions & 9 deletions backbone.epoxy-extras.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Backbone.Epoxy -- Extras
// Backbone.Epoxy -- Extras v0.0.0
// Additional Epoxy handlers and operators for mixing into core.

// (c) 2013 Greg MacWilliam
Expand All @@ -15,14 +15,13 @@

if (!Epoxy) throw( "Backbone.Epoxy not found." );

var bindingOptions = Epoxy.View.bindingOptions;
var readAccessor = Epoxy.View.readAccessor;
var makeOperator = Epoxy.View.makeOperator;


var bindingOptions = Epoxy.binding.options;
var readAccessor = Epoxy.binding.readAccessor;
var makeOperator = Epoxy.binding.makeOperator;

// Binding Handlers
// ----------------
_.extend(Epoxy.View.bindingHandlers, {
_.extend(Epoxy.binding.handlers, {

readonly: {
set: function( $element, value ) {
Expand All @@ -40,9 +39,8 @@

// Binding Operators
// -----------------
_.extend(Epoxy.View.bindingOperators, {
_.extend(Epoxy.binding.operators, {

});


}).call( this );
96 changes: 45 additions & 51 deletions backbone.epoxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -470,8 +470,8 @@
});


// Epoxy.View -> Binding API
// -------------------------
// Epoxy.binding -> Binding API
// ----------------------------

// Reads value from an accessor:
// Accessors come in three potential forms:
Expand Down Expand Up @@ -627,7 +627,7 @@

} else {
// Reset with new views:
this.wipe();
this.clean();

collection.each(function( model ) {
views[ model.cid ] = view = new collection.view({model: model});
Expand All @@ -639,7 +639,7 @@
$element.show();
}
},
wipe: function() {
clean: function() {
for (var id in this.v) {
if ( this.v.hasOwnProperty( id ) ) {
this.v[ id ].remove();
Expand Down Expand Up @@ -746,33 +746,18 @@
}
},

// Template: write-only. Renders the bound element into an Underscore template.
// Template: write-only. Renders the bound element with an Underscore template.
template: {
init: function() {
this.t = {};
init: function( $element, value ) {
var raw = $element.find("script,template");
this.tmpl = _.template( raw.length ? raw.html() : $element.html() );
},
set: function( $element, value ) {
// Extract data and template references:
var data = value.data;
var tmpl = value.tmpl;

// Pull model data, if applicable:
data = isModel(data) ? data.toJSON({obs:true}) : data;

// Create and cache template definitions:
if ( !isFunction(tmpl) ) {
tmpl = this.t[ tmpl ] = this.t[ tmpl ] ? this.t[ tmpl ] : _.template($(tmpl).html());
}

// Render template into element:
try {
$element.html( tmpl(data) );
} catch( error ) {
// do nothing in response to errors...
// this may occur if model/template definitions are swapped.
// New model/template will register one at a time, and may not mesh.
// No good way around this, other than to suggest against hot template swaps.
}
value = isModel(value) ? value.toJSON({obs:true}) : value;
$element.html( this.tmpl(value) );
},
clean: function() {
this.tmpl = null;
}
},

Expand Down Expand Up @@ -892,6 +877,16 @@
};


// Define binding API:
Epoxy.binding = {
handlers: bindingHandlers,
operators: bindingOperators,
options: bindingOptions,
makeOperator: makeOperator,
readAccessor: readAccessor
};


// Epoxy.View
// ----------
var viewMap;
Expand Down Expand Up @@ -937,12 +932,12 @@
});

// Add native "model" and "collection" data sources:
self.model = view_addSourceToContext( self.model, context );
self.collection = view_addSourceToContext( self.collection, context );
self.model = addSourceToViewContext( self.model, context );
self.collection = addSourceToViewContext( self.collection, context );

// Add all additional data sources:
_.each(sources, function( source, sourceName ) {
sources[ sourceName ] = view_addSourceToContext( source, context, sourceName );
sources[ sourceName ] = addSourceToViewContext( source, context, sourceName );
});

// Create all bindings:
Expand All @@ -955,11 +950,11 @@

_.each(declarations, function( elementDecs, selector ) {
// Get DOM jQuery reference:
var $element = view_queryForSelector( self, selector );
var $element = queryViewForSelector( self, selector );

// Ignore empty DOM queries (without errors):
if ( $element.length ) {
view_bindElement( self, $element, elementDecs, context, handlers );
bindElementToView( self, $element, elementDecs, context, handlers );
}
});

Expand All @@ -969,9 +964,9 @@
// <span data-bind="text:attribute"></span>

// Create bindings for each matched element:
view_queryForSelector( self, "["+declarations+"]" ).each(function() {
queryViewForSelector( self, "["+declarations+"]" ).each(function() {
var $element = $(this);
view_bindElement( self, $element, $element.attr(declarations), context, handlers );
bindElementToView( self, $element, $element.attr(declarations), context, handlers );
});
}
},
Expand All @@ -990,14 +985,6 @@
viewSuper( this, "remove" );
}

}, {
// Define core components as static properties of the view:
// these components are available through the "Epoxy.View" namespace for extension.
bindingHandlers: bindingHandlers,
bindingOperators: bindingOperators,
bindingOptions: bindingOptions,
makeOperator: makeOperator,
readAccessor: readAccessor
});

// Epoxy.View -> Private
Expand All @@ -1007,7 +994,7 @@
// Data sources are Backbone.Model and Backbone.Collection instances.
// @param source: a source instance, or a function that returns a source.
// @param context: the working binding context. All bindings in a view share a context.
function view_addSourceToContext( source, context, name ) {
function addSourceToViewContext( source, context, name ) {

// Ignore missing sources, and invoke non-instances:
if (!source) return;
Expand Down Expand Up @@ -1073,7 +1060,7 @@

// Queries element selectors within a view:
// matches elements within the view, and the view's container element.
function view_queryForSelector( view, selector ) {
function queryViewForSelector( view, selector ) {
var $elements = view.$( selector );

// Include top-level view in bindings search:
Expand All @@ -1091,19 +1078,19 @@
// @param declarations: the string of binding declarations provided for the element.
// @param context: a compiled binding context with all availabe view data.
// @param handlers: a compiled handlers table with all native/custom handlers.
function view_bindElement( view, $element, declarations, context, handlers ) {
function bindElementToView( view, $element, declarations, context, handlers ) {

// Parse localized binding context:
// parsing function is invoked with "operators" and "context" properties made available,
// yeilds a native context object with element-specific bindings defined.
try {
context = new Function("$o","$c","with($o){with($c){return{"+ declarations +"}}}")(bindingOperators, context);
var localContext = new Function("$o","$c","with($o){with($c){return{"+ declarations +"}}}")(bindingOperators, context);
} catch ( error ) {
throw( "Error parsing bindings: "+declarations );
}

// Pick out special binding options from the main context:
var options = _.pick(context, bindingOptions);
var options = _.pick(localContext, bindingOptions);

// Format the "events" option:
// include events from the binding declaration along with a default "change" trigger,
Expand All @@ -1112,8 +1099,15 @@
return name+".epoxy";
}).join(" ");


// If an array of template attributes was provided,
// pick specified accessors out of the main context for use in a template binding:
if ( localContext.template && isArray(localContext.template) ) {
localContext.template = _.pick(context, localContext.template);
}

// Apply bindings from native context:
_.each(_.omit(context, bindingOptions), function( accessor, handlerName ) {
_.each(_.omit(localContext, bindingOptions), function( accessor, handlerName ) {

// Validate that each defined handler method exists before binding:
if ( handlers.hasOwnProperty(handlerName) ) {
Expand Down Expand Up @@ -1184,12 +1178,12 @@
init: blankMethod,
get: blankMethod,
set: blankMethod,
wipe: blankMethod,
clean: blankMethod,

// Destroys the binding:
// all events and managed sub-views are killed.
dispose: function() {
this.wipe();
this.clean();
this.stopListening();
this.$el.off( this.events );
this.$el = null;
Expand Down
2 changes: 1 addition & 1 deletion backbone.epoxy.min.js

Large diffs are not rendered by default.

14 changes: 8 additions & 6 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@
<p><select class="test-select-multi" multiple="multiple" data-bind="value:valMulti,options:optionsList"></select></p>
</div>
<div class="section">
<div class="test-template"></div>
<div class="test-template" data-bind="template:{first:firstName,last:lastName}">
<script type="text/tmpl"><p>{{ first }} {{ last }}</p></script>
</div>
</div>
<div class="section">
<strong>Collection Management</strong>
Expand All @@ -132,17 +134,17 @@
</div>
</script>

<script type="text/template-epoxy" id="test-template-tmpl">

</script>

<script src="../www/js/jquery.js"></script>
<script src="../www/js/lodash.js"></script>
<script src="../www/js/underscore.js"></script>
<script src="../www/js/backbone.js"></script>
<script src="../backbone.epoxy.js"></script>
<script src="../backbone.epoxy-extras.js"></script>
<script>
_.templateSettings = { interpolate : /\{\{(.+?)\}\}/g };
</script>
<script src="epoxy-test.js"></script>
<script type="text/javascript">

$(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
Expand Down
Loading

0 comments on commit 0d73ff0

Please sign in to comment.