Skip to content

Commit

Permalink
Commit 52 (Beta Candidate)
Browse files Browse the repository at this point in the history
Note that this update brings significant feature improvements
with corresponding changes to the implementation. For normal
usage patterns these do not correspond to breaking changes. However
there could possibly be breaking changes on a few advanced scenarios.
If appropriate, please file issues if breaking changes are
observed. See also the samples and unit tests for changes that take
advantage of new features below.

New features and improvements
- {{: xxx}} can now be used without arguments, so {{:}} is equivalent
  to {{:#data}}. This is a similar pattern to {{for}} {{include}} etc.

- autoBind feature for custom tags allows a custom tag without
  argument to bind to current data context, so {{mytag/}} is
  equivalent to {{mytag #data/}}. This is achieved by setting
  autoBind: true on the tag definition.

- Can now define a converter for the first arg on any tag - custom
  or built-in. e.g. {{for myarray convert=~reverseArray}}
  or {{mycustomtag name convert='someRegisteredConverter'/}}
  or <div data-link="name convert=formatName" /}} (in this example
  the person object has a name property and a formatName method)

- Similarly any tag with two-way binding can have a convertBack
  converter:
  e.g. <input data-link="name convert=~SomeHelper convertBack=SomeMethodOnDataObject" />
  or {^{slider speed convert=~fromMphToKmH convertBack=fromKmHtoMph/}}
  or: {^{myTextBoxTag convert=~myCnvtHelper convertBack='myregisteredConverter'/}}

- Support for triggering updates to the tag based on dependencies
  declared for the converter as well as dependencies declared on
  the tag itself. There is also a new $.view.sub.getDeps() helper
  function for combining multiple dependency declarations - used
  internally to implement the above feature.

- Improved support for rendering arrays without iteration:
  render(array, helpers) will iterate over the array, but you can
  now pass a 'noIteration' boolean. So render(array, true) renders
  anv array without iteration, and similarly, render(array, helpers, true)
  now renders an array without iteration while passing in helpers.

  Within methods and events of custom tags:
  tagCtx.render() renders against the current data object,
  tagCtx.render(someArray) iterates over the array
  tagCtx.render(someArray, true) or tagCtx.render(someArray, helpers, true)
  renders without iteration.

- In a custom tag, a template can now be assigned (determined
  dynamically) in the init method simply by setting:
  this.template = templateObjectOrMarkupString;
  It is no longer necessary to write:
  this.template = tagCtx.tmpl = templateObjectOrMarkupString;

- Support for declaring event handlers directly on tags or
  data-link expressions: {{anyTag onXxx=myXxxHandler /}}
  This can be used for standard event handlers such as onUpdate,
  onBeforeChange etc.
  e.g. <textarea data-link="text onUpdate=updateMethodOnViewModelObject"></textarea>

- There is a new helper utility function for validation of
  HTML markup in JsViews: $.views.utility.validate(markupString)
  This could be used in the following example:
  $.views.helpers("validateMarkup", $.views.utility.validate);
  <textarea data-link="text onBeforeChange=~validateMarkup"></textarea>

- Improved error message if JsViews or JsObservable is loaded without jQuery

Bug fixes:
- Fix for BorisMoore/jsrender#231
- Fix for BorisMoore#198
- Fix for BorisMoore#231

- Fix for a memory leak issue

- Fixes for some issues with radio buttons and with multiselect

- Fixes for bugs in some samples
  • Loading branch information
BorisMoore committed Mar 25, 2014
1 parent 354189f commit fe24e43
Show file tree
Hide file tree
Showing 15 changed files with 1,065 additions and 672 deletions.
74 changes: 44 additions & 30 deletions jquery.observable.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*! JsObservable v1.0.0-alpha: http://github.com/BorisMoore/jsviews and http://jsviews.com/jsviews
informal pre V1.0 commit counter: 51 (Beta Candidate) */
informal pre V1.0 commit counter: 52 (Beta Candidate) */
/*
* Subcomponent of JsViews
* Data change events for data-linking
*
* Copyright 2013, Boris Moore
* Copyright 2014, Boris Moore
* Released under the MIT License.
*/

Expand All @@ -14,15 +14,14 @@ informal pre V1.0 commit counter: 51 (Beta Candidate) */
"use strict";

if (!$) {
throw "requires jQuery or JsRender";
throw "jsViews/jsObservable require jQuery";
}
if ($.observable) { return; } // JsObservable is already loaded

//========================== Top-level vars ==========================

var versionNumber = "v1.0.0-alpha",

currentCbBindings, currentCbBindingsId,
$eventSpecial = $.event.special,
$viewsSub = $.views
? $.views.sub // jsrender was loaded before jquery.observable
Expand All @@ -44,6 +43,23 @@ informal pre V1.0 commit counter: 51 (Beta Candidate) */

//========================== Top-level functions ==========================

$viewsSub.getDeps = function() {
var args = arguments;
return function() {
var arg, dep,
deps = [],
l=args.length;
while (l--) {
arg = args[l--],
dep = args[l];
if (dep) {
deps = deps.concat($isFunction(dep) ? dep(arg, arg) : dep);
}
}
return deps;
}
}

function $observable(data) {
return $isArray(data)
? new ArrayObservable(data)
Expand Down Expand Up @@ -99,7 +115,7 @@ informal pre V1.0 commit counter: 51 (Beta Candidate) */
// If the cbBindings collection is empty we will remove it from the cbBindingsStore
var cb, found;

for(cb in cbBindings) {
for (cb in cbBindings) {
found = true;
break;
}
Expand Down Expand Up @@ -154,16 +170,9 @@ informal pre V1.0 commit counter: 51 (Beta Candidate) */
var j, evData,
obIdExpando = $hasData(object),
boundObOrArr = wrapArray(object);
currentCbBindings = 0;
if (unobserve || off) {
if (obIdExpando) {
$(boundObOrArr).off(namespace, onObservableChange);
// jQuery off event does not provide the event data, with the callback and we need to remove this object from the corresponding cbBindings hash, cbBindingsStore[cb._cId].
// So we have registered a jQuery special 'remove' event, which stored the cbBindingsStore[cb._cId] cbBindings hash in the currentCbBindings var,
// so we can immediately remove this object from that cbBindings hash.
if (currentCbBindings) {
delete currentCbBindings[$.data(object, "obId")];
}
}
} else {
if (events = obIdExpando && $._data(object)) {
Expand All @@ -178,10 +187,6 @@ informal pre V1.0 commit counter: 51 (Beta Candidate) */
return;
} else if (pathStr === "*" && data.prop !== pathStr || data.prop === prop) {
$(object).off(namespace, onObservableChange);
// We remove this object from cbBindings hash (see above).
if (currentCbBindings) {
delete currentCbBindings[$.data(object, "obId")];
}
}
}
}
Expand Down Expand Up @@ -809,20 +814,29 @@ informal pre V1.0 commit counter: 51 (Beta Candidate) */
};

$eventSpecial[propertyChangeStr] = $eventSpecial[arrayChangeStr] = {
// 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._cId from the event here and provide the corresponding currentCbBindings hash via the
// currentCbBindings var to the unobserve handler, so we can immediately remove this object from that bindings hash, after 'unobserving'.
remove: function(evData) {
if ((evData = evData.data) && (evData.off = 1, evData = evData.cb)) { //Set off=1 as marker for disposed event
// Get the cb._cId from ev.data.cb._cId
currentCbBindings = cbBindingsStore[currentCbBindingsId = evData._cId];
}
},
teardown: function(namespaces) {
if (currentCbBindings) {
delete currentCbBindings[$.data(this, "obId")];
removeCbBindings(currentCbBindings, currentCbBindingsId);
// Register a jQuery special 'remove' event, to access the data associated with handlers being removed by jQuery.off().
// We get data.cb._cId from the event handleObj and get the corresponding cbBindings hash from the cbBindingsStore,
// then remove this object from that bindings hash - if the object does not have any other handlers associated with the same callback.
remove: function (handleObj) {
var cbBindings, found, events, l, data,
evData = handleObj.data;
if ((evData) && (evData.off = 1, evData = evData.cb)) { //Set off=1 as marker for disposed event
// Get the cb._cId from handleObj.data.cb._cId
if (cbBindings = cbBindingsStore[evData._cId]) {
// There were bindings for this callback. If this was the last one, we'll remove it.
events = $._data(this).events[handleObj.type];
l = events.length;
while (l-- && !found) {
found = (data = events[l].data) && data.cb === evData; // Found another one with same callback
}
if (!found) {
// This was the last handler for this callback and object, so remove the binding entry
delete cbBindings[$.data(this, "obId")];
removeCbBindings(cbBindings, evData._cId);
}
}
}
}
};
})(this, this.jQuery || this.jsviews);

})(this, this.jQuery);
6 changes: 3 additions & 3 deletions jquery.observable.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions jquery.observable.min.js.map

Large diffs are not rendered by default.

Loading

0 comments on commit fe24e43

Please sign in to comment.