Skip to content
This repository has been archived by the owner on Sep 14, 2019. It is now read-only.

Commit

Permalink
added a previewError hook for bindings to deal with validation errors…
Browse files Browse the repository at this point in the history
… on sets
  • Loading branch information
dzrw committed Feb 27, 2012
1 parent 8e550d4 commit aeb43d7
Showing 1 changed file with 83 additions and 31 deletions.
114 changes: 83 additions & 31 deletions outback.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,40 +34,57 @@
}

function makeValueAccessorBuilder(model) {
return function(binding) {
var defaultOptions, modelAttrName;
function optionsFor(args) {
var options;

_.extend(options = {}, {
escape: false,
silent: false,
readMethod: 'get',
previewError: false
});

defaultOptions = {
escape: false
};
if (args && args.length === 1) {
_.extend(options, args[0]);
}

options.readMethod = options.escape ? 'escape' : 'get';
return options;
}

return function(binding) {
var modelAttrName;
modelAttrName = binding.modelAttrName;

var valueAccessor = function() {
var args, options, readMethod;

_.extend(options = {}, defaultOptions);

args = Array.prototype.slice.call(arguments);
if (args.length === 1) {
_.extend(options, args[0]);
}
var options;
options = optionsFor(Array.prototype.slice.call(arguments));

if (!!options.parents) {
return binding.parents;
}

readMethod = options.escape ? 'escape' : 'get';

var value = function() {
var args, modelAttr;
var args, modelAttr, changeSet, changeSetOptions;
args = Array.prototype.slice.call(arguments);
if (args.length === 0) {
modelAttr = model[readMethod](modelAttrName);
modelAttr = model[options.readMethod](modelAttrName);
return modelAttr;
} else {
modelAttr = args[0];
model.set(modelAttrName, modelAttr);
changeSet = {};
changeSet[modelAttrName] = args[0];

changeSetOptions = {};

if (!!options.silent) {
changeSetOptions.silent = true;
}

if (!!options.previewError) {
changeSetOptions.error = binding.previewError;
}

model.set(changeSet, changeSetOptions);
}
};

Expand All @@ -82,22 +99,18 @@
var subscribe, unsubscribe, valueAccessor;

subscribe = function(eventName, callback) {
var eventName;
eventName = eventName || "change:" + modelAttrName;
model.on(eventName, callback);
};

unsubscribe = function(eventName) {
var eventName;
eventName = eventName || "change:" + modelAttrName;
model.off(eventName);
};

return {
modelAttrName: modelAttrName,
valueAccessor: makeValueAccessorBuilder(model),
modelEvents: {
eventName: false, // TODO: Defaults to "change:modelAttrName"
eventName: "change:" + modelAttrName,
subscribe: subscribe,
unsubscribe: unsubscribe
}
Expand Down Expand Up @@ -245,6 +258,20 @@
return executableBindings;
}

function improveExecutableBinding(binding, view) {
if (hop(binding.handler, 'previewError')) {
binding.previewError = function(model, error) {
var binderArgs, e, preventDefault;
e = {error: error, preventDefault: false};
binderArgs = [binding.element, binding.valueAccessor, binding.allBindingsAccessor, view, e];
binding.handler.previewError.apply(view, binderArgs);
if (!e.preventDefault) {
model.trigger('error', error);
}
};
}
}

function applyBinding (view, binding) {
var binders, binderArgs, eventName, updateFn;

Expand Down Expand Up @@ -345,6 +372,10 @@
arrayConcat(bindings, filterExecutableBindings(bindingDecl, bindingHandlers));
});

_.each(bindings, function (binding) {
improveExecutableBinding(binding, view);
});

if (typeof view.previewBinding === 'function') {
bindings = _.filter(bindings, function(binding) {
return view.previewBinding(binding);
Expand Down Expand Up @@ -525,7 +556,7 @@
/* The "css" binding
Usage:
data-bind="css: { class1: @modelAttr1, class2: @modelAttr2, class2Options: { not: <truthy> } }"
data-bind="css: { class1: @modelAttr1, class2: @modelAttr2, class2Options: { not: <truthy>, on: <string> } }"
@modelAttr is interpreted as truthy or falsy
Expand Down Expand Up @@ -690,14 +721,17 @@
/* The "value" binding
Usage:
data-bind="value: @modelAttr, valueOptions { valueUpdate: 'eventName', escape: <truthy> }"
data-bind="value: @modelAttr, valueOptions { valueUpdate: 'eventName', escape: <truthy>, silent: <truthy> }"
valueUpdate defaults to 'change' if not specified
escape controls whether or not an HTML-escaped version of a model's
attribute is used. Using escape to retrieve attributes will
prevent XSS attacks. The default is true.
silent determines whether setting the model triggers validation.
The default is false.
Purpose: The value binding links the associated DOM element’s value
with a property on your view model. This is typically useful with
form elements such as <input>, <select> and <textarea>.
Expand All @@ -708,7 +742,9 @@

config = {
eventName: 'change',
escape: true
escape: true,
silent: false,
previewError: true,
};

options = allBindingsAccessor('valueOptions');
Expand All @@ -719,33 +755,49 @@
if(options && hop(options, 'valueUpdate')) {
config.eventName = options.valueUpdate;
}

if(options && hop(options, 'silent')) {
config.silent = !!options.silent;
}

return config;
}

return {
init: function (element, valueAccessor, allBindingsAccessor, view) {
var config;
var config, writeOptions;
config = optionsFor(valueAccessor, allBindingsAccessor);

writeOptions = {
silent: config.silent,
previewError: config.previewError
};

$(element).on(config.eventName, function (e) {
var value;
value = $(element).val();
valueAccessor()(value);
valueAccessor(writeOptions)(value);
});
},
update: function (element, valueAccessor, allBindingsAccessor, view) {
var config, value;
var config, value, readOptions;
config = optionsFor(valueAccessor, allBindingsAccessor);

value = valueAccessor({escape: config.escape})();
readOptions = {escape: config.escape};

value = valueAccessor(readOptions)();
$(element).val(value);
},
remove: function (element, valueAccessor, allBindingsAccessor, view) {
var config;
config = optionsFor(valueAccessor, allBindingsAccessor);

$(element).off(config.eventName);
},
previewError: function (element, valueAccessor, allBindingsAccessor, view, e) {
var error = e.error;
// TODO: Do something useful.
e.preventDefault = false;
}
}
})();
Expand Down

0 comments on commit aeb43d7

Please sign in to comment.