Permalink
Browse files

Commit 25.

- Major changes internally for data-binding of views and tag controls.
- New view.get() API for finding parent/child views by type.
- Renamed view._hlp to view.hlp - public API to find contextual helpers/params.
- New and improved tag and view hierarchy navigation APIs, including
- view.childTags() and tag.childTags() for finding descendant tags
(optionally filtered by tagName).
- New tag.flow boolean property on tags. If true, ignored by tag hierarchy
navigation APIs.
- New ~root helper in templates to access the top-level (root) data from
nested contexts.
- view.renderContent() now automatically uses the current data context and
parentView
- Added support for data-linking to array.length on an observable array.
- Many new unit tests added, especially for new view and tag navigation APIs

- Includes fixes for #142 #135 #122 #132 and possibly #121

- Fixed bugs on radio-button data-linking
  • Loading branch information...
1 parent ef52562 commit 14bb7ef9d724c885edfe2cd474de786b4d39c55d @BorisMoore committed Jan 17, 2013
@@ -20,7 +20,7 @@
immediately after the highest-level item to be 'observed'.
<br /><br />For paths in which objects higher in the path may sometimes be null or undefined, add <b>noerror = true</b> in order to suppress error rendering and default to an empty string.
-<br /><br />Example:<b>&lt;div data-link="selected^name.first noerror=true">&lt;div></b></div>
+<br /><br />Example: <b>&lt;div data-link="selected^name.first noerror=true">&lt;div></b></div>
<!--====== Containers ======-->
<div class="box">
@@ -28,25 +28,25 @@
{{/for}}
</select>
- <div data-link="visible{bool:selectedMovie!=='none' }">
+ <div data-link="visible{:selectedMovie!=='none' }">
<div class="subhead" data-link="{:name ? 'Name:' : 'Type your name here:'}"></div>
<div><input data-link="name" placeholder="Enter your name..."/></div>
- <div data-link="visible{bool:name}">
- <div class="subhead" data-link="{:request ? 'Special requests:' : 'Type special requests here:'} visible{bool:name}"></div>
+ <div data-link="visible{:!!name}">
+ <div class="subhead" data-link="{:request ? 'Special requests:' : 'Type special requests here:'} visible{:!!name}"></div>
<div><textarea data-link="request" rows="4" cols="25" placeholder="Enter any special requests..."></textarea></div>
</div>
</div>
</div>
- <div data-link="visible{bool:name && selectedMovie!=='none'}">
+ <div data-link="visible{:name && selectedMovie!=='none'}">
<h3>Ticket order form</h3>
- <div class="box" data-link="visible{bool:name}">
+ <div class="box" data-link="visible{:!!name}">
<div>Ticket for <em data-link="name" ></em> to movie: <em data-link="~selectedTitle(selectedMovie)" ></em></div>
- <div data-link="visible{bool:request}"><em>Your special request:</em> <pre data-link="request" ></pre></div>
+ <div data-link="visible{:!!request}"><em>Your special request:</em> <pre data-link="request" ></pre></div>
<div>
<b>Ticket Price:</b> <span data-link="~convertedPrice(selectedMovie, selectedCurrency)" ></span>
<div>Choose currency for payment: <input type="checkbox" data-link="~app.chooseCurrency" /></div>
@@ -56,7 +56,7 @@
</div>
</div>
- <div class="box" data-link="visible{bool:~app.chooseCurrency}">
+ <div class="box" data-link="visible{:~app.chooseCurrency}">
<div class="subhead">Choose Currency:</div>
{{for ~currencies ~details=#data}}
<input type="radio" name="currencyPicker" value="{{:#index}}" data-link="~details.selectedCurrency" />{{:label}}<br/>
@@ -98,10 +98,10 @@
contextHelpers = {
app: {},
movies: movies,
- convertedPrice: function(selected, selectedCurrency) {
+ convertedPrice: function(selectedMovie, selectedCurrency) {
var currency = currencies[selectedCurrency];
- if ( selected !== "none") {
- return currency.symbol + parseFloat(movies[selectedCurrency].ticketPrice * currency.rate).toFixed(2);
+ if ( selectedMovie !== "none") {
+ return currency.symbol + parseFloat(movies[selectedMovie].ticketPrice * currency.rate).toFixed(2);
}
},
currencies: currencies,
@@ -110,14 +110,6 @@
}
};
- $.views.converters({
- // Converts values to "true" or "false" in rendered content.
- // So the string "false" evaluates to true, and "" evaluates to false. - Used in data-link="visible{bool:name}", for example.
- bool: function (value) {
- return !!value;
- }
- });
-
$.templates( "moviePurchaseTemplate", "#moviePurchaseTemplate" );
$.link.moviePurchaseTemplate( "#moviePurchase", orderDetails, contextHelpers );
@@ -76,9 +76,9 @@
},
render: function() {
if (!this.contentTmpl) {
- this.contentTmpl = this.tmpl;
- this.tmpl = this.template;
- this.labels = $(this.contentTmpl.markup).filter("h1").map(function() {
+ this.contentTmpl = this._.tmpl;
+ this._.tmpl = this.template;
+ this.labels = $($.trim(this.contentTmpl.markup)).filter("h1").map(function() {
return this.innerText || this.textContent;
}).get();
}
View
@@ -6,7 +6,7 @@
* Copyright 2012, Boris Moore and Brad Olenick
* Released under the MIT License.
*/
-// informal pre beta commit counter: 24b
+// informal pre beta commit counter: 25
// TODO, Array change on leaf. Caching compiled templates.
// TODO later support paths with arrays ~x.y[2].foo, paths with functions on non-leaf tokens: address().street
@@ -25,8 +25,9 @@
var versionNumber = "v1.0pre",
- $viewsSettings = $.views ? $.views.settings : {},
- cbBindings,
+ $viewsSettings = $.views ? $.views.settings : {}, cbBindings, oldLength, _data,
+ cbBindingsStore = {},
+ cbBindingKey = 1,
splice = [].splice,
concat = [].concat,
$isArray = $.isArray,
@@ -88,7 +89,7 @@
if ($isFunction(path)) {
// TODO add support for _parameterized_ calls to depends() on computed observables. Consider getting args by a
// compiled version of linkFn that just returns the current args. args = linkFnArgs.call(linkCtx, target, view, $views);
- splice.apply(out, [out.length,1].concat(resolvePathObjects.call(this, path.call(this, root), root)));
+ splice.apply(out, [out.length,1].concat(resolvePathObjects(path(root), root)));
continue;
} else if ("" + path !== path) {
root = nextObj = path;
@@ -106,10 +107,10 @@
var ctx = ev.data;
if (ctx.prop === "*" || ctx.prop === eventArgs.path) {
if (typeof eventArgs.oldValue === OBJECT) {
- $unobserve.call(ctx.linkCtx, eventArgs.oldValue, ctx.path, ctx.cb);
+ $unobserve(eventArgs.oldValue, ctx.path, ctx.cb);
}
if (typeof eventArgs.value === OBJECT) {
- $observe.call(ctx.linkCtx, eventArgs.value, ctx.path, ctx.cb, ctx.root);
+ $observe(eventArgs.value, ctx.path, ctx.cb, ctx.root);
}
ctx.cb.call(ctx.root, ev, eventArgs);
}
@@ -119,9 +120,10 @@
// The jQuery 'off' method does not provide the event data from the event(s) that are being unbound, so we register
// a jQuery special 'remove' event, and get the data.cb._bnd from the event here and provide it in the
// cbBindings var to the unobserve handler, so we can immediately remove this object from that cb._bnd collection, after 'unobserving'.
- remove: function(handleObj) {
- if ((handleObj = handleObj.data) && (handleObj = handleObj.cb)) {
- cbBindings = handleObj._bnd;
+ remove: function(evData) {
+ if ((evData = evData.data) && (evData = evData.cb)) {
+ // Get the cb._bnd from the ev.data object
+ cbBindings = cbBindingsStore[evData._bnd];
}
}
};
@@ -154,7 +156,7 @@
}
}
}
- $($isArray(object) ? [object] : object).on(namespace, null, {linkCtx: linkCtx, root: origRoot, path: pathStr, prop: prop, cb: callback}, onObservableChange);
+ $($isArray(object) ? [object] : object).on(namespace, null, {path: pathStr, prop: prop, cb: callback}, onObservableChange);
if (bindings) {
// Add object to bindings, and add the counter to the jQuery data on the object
obIdExpando = object[$expando];
@@ -170,7 +172,6 @@
lastArg = paths.pop(),
origRoot = paths[0],
root = "" + origRoot !== origRoot ? paths.shift() : undefined, // First parameter is the root object, unless a string
- linkCtx = this,
l = paths.length;
origRoot = root;
@@ -204,14 +205,16 @@
cbNs = callback && callback.cbNs;
if (unobserve && l === 0 && root) {
- // unobserve(object) TODO what if there is a callback specified
+ // unobserve(object) TODO: What if there is a callback specified?
$(root).off(observeStr, onObservableChange);
}
- bindings = callback
- ? topLevel
- ? (callback._bnd = callback._bnd || {})
- : callback._bnd
- : undefined;
+ if (callback) {
+ bindings = callback._bnd = callback._bnd ||
+ // This will be a top-level call for a new callback
+ cbBindingKey++;
+
+ bindings = cbBindingsStore[bindings] = cbBindingsStore[bindings] || {};
+ }
depth = 0;
for (i = 0; i < l; i++) {
@@ -268,7 +271,7 @@
// prop = "*";
if ($isFunction(object)) {
if (dep = object.depends) {
- $observe.call(this, dep, callback, unobserve||origRoot);
+ $observe(dep, callback, unobserve||origRoot);
}
} else {
observeOnOff(ns, prop);
@@ -289,7 +292,7 @@
if ($isFunction(prop)) {
if (dep = prop.depends) {
// This is a computed observable. We will observe any declared dependencies
- $observe.call(this, object, resolvePathObjects.call(this, dep, object), callback, filter, unobserve||origRoot);
+ $observe(object, resolvePathObjects(dep, object), callback, filter, unobserve||origRoot);
}
break;
}
@@ -409,7 +412,9 @@
},
_insert: function(index, data) {
- splice.apply(this._data, [index, 0].concat(data));
+ _data = this._data;
+ oldLength = _data.length;
+ splice.apply(_data, [index, 0].concat(data));
this._trigger({change: "insert", index: index, items: data});
},
@@ -428,7 +433,9 @@
},
_remove: function(index, numToRemove, items) {
- this._data.splice(index, numToRemove);
+ _data = this._data;
+ oldLength = _data.length;
+ _data.splice(index, numToRemove);
this._trigger({change: "remove", index: index, items: items});
},
@@ -445,8 +452,10 @@
},
_move: function(oldIndex, newIndex, numToMove, items) {
- this._data.splice( oldIndex, numToMove );
- this._data.splice.apply( this._data, [ newIndex, 0 ].concat( items ) );
+ _data = this._data;
+ oldLength = _data.length;
+ _data.splice( oldIndex, numToMove );
+ _data.splice.apply( _data, [ newIndex, 0 ].concat( items ) );
this._trigger( { change: "move", oldIndex: oldIndex, index: newIndex, items: items } );
},
@@ -457,12 +466,16 @@
},
_refresh: function(oldItems, newItems) {
- splice.apply(this._data, [0, this._data.length].concat(newItems));
+ _data = this._data;
+ oldLength = _data.length;
+ splice.apply(_data, [0, _data.length].concat(newItems));
this._trigger({change: "refresh", oldItems: oldItems});
},
_trigger: function(eventArgs) {
- $([this._data]).triggerHandler(arrayChangeStr, eventArgs);
+ var $data = $([_data]);
+ $data.triggerHandler(arrayChangeStr, eventArgs);
+ $data.triggerHandler(propertyChangeStr, {path: "length", value: _data.length, oldValue: oldLength});
}
};
})(this, this.jQuery || this.jsviews);
Oops, something went wrong.

0 comments on commit 14bb7ef

Please sign in to comment.