Change {immediate: ...} to {priority: "deferred|immediate|event"}

This was the product of a long discussion between @wch, @alandipert, @bborgesr
and myself. The conflation of immediate (no throttle/debounce) and non-dedupe
in a single "immediate" flag was deemed unacceptable, because UI controls often
want immediacy but also dedupe. Introducing a second "dedupe" flag would work
but {immediate: false, dedupe: false} doesn't make much sense, and dedupe not
only implies that InputNoResendDecorator should behave differently but also
InputBatchSender (i.e. no deduplication AND no coalescing).

We decided to remove the "immediate" boolean option and replace it with a
string option that would have three possibilities at this time. The only con
to this approach is if anyone is calling onInputChange with immediate:true
today, and I can't imagine anyone is. The immediate flag only has any effect
if the input id that's being set has been put in debounce/throttle mode, and
I don't even think that is documented today, and I'm not even sure it's
possible to do it from custom JS (that's not part of a custom input binding).
This commit is contained in:
Joe Cheng
2018-04-17 16:39:05 -07:00
parent 3ccf2937b4
commit c555725201
6 changed files with 61 additions and 25 deletions

View File

@@ -766,7 +766,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
this.pendingData[name] = value;
if (!this.reentrant) {
if (opts.immediate) {
if (opts.priority === "event") {
this.$sendNow();
} else if (!this.timerId) {
this.timerId = setTimeout(this.$sendNow.bind(this), 0);
@@ -807,7 +807,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
var jsonValue = JSON.stringify(value);
if (!opts.immediate && this.lastSentValues[inputName] && this.lastSentValues[inputName].jsonValue === jsonValue && this.lastSentValues[inputName].inputType === inputType) {
if (opts.priority !== "event" && this.lastSentValues[inputName] && this.lastSentValues[inputName].jsonValue === jsonValue && this.lastSentValues[inputName].inputType === inputType) {
return;
}
this.lastSentValues[inputName] = { jsonValue: jsonValue, inputType: inputType };
@@ -854,7 +854,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
evt.value = value;
evt.binding = opts.binding;
evt.el = opts.el;
evt.immediate = opts.immediate;
evt.priority = opts.priority;
$(document).trigger(evt);
@@ -862,9 +862,9 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
name = evt.name;
if (evt.inputType !== '') name += ':' + evt.inputType;
// opts aren't passed along to lower levels in the input decorator
// Most opts aren't passed along to lower levels in the input decorator
// stack.
this.target.setInput(name, evt.value, { immediate: opts.immediate });
this.target.setInput(name, evt.value, { priority: opts.priority });
}
};
}).call(InputEventDecorator.prototype);
@@ -877,7 +877,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
this.setInput = function (name, value, opts) {
this.$ensureInit(name);
if (opts.immediate) this.inputRatePolicies[name].immediateCall(name, value, opts);else this.inputRatePolicies[name].normalCall(name, value, opts);
if (opts.priority !== "deferred") this.inputRatePolicies[name].immediateCall(name, value, opts);else this.inputRatePolicies[name].normalCall(name, value, opts);
};
this.setRatePolicy = function (name, mode, millis) {
if (mode === 'direct') {
@@ -929,11 +929,25 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
// Merge opts with defaults, and return a new object.
function addDefaultInputOpts(opts) {
return $.extend({
immediate: false,
opts = $.extend({
priority: "immediate",
binding: null,
el: null
}, opts);
if (opts && typeof opts.priority !== "undefined") {
switch (opts.priority) {
case "deferred":
case "immediate":
case "event":
break;
default:
throw new Error("Unexpected input value mode: '" + opts.priority + "'");
}
}
return opts;
}
function splitInputNameType(name) {
@@ -6072,7 +6086,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
inputs = new InputValidateDecorator(inputs);
exports.onInputChange = function (name, value, opts) {
exports.setInputValue = exports.onInputChange = function (name, value, opts) {
opts = addDefaultInputOpts(opts);
inputs.setInput(name, value, opts);
};
@@ -6086,7 +6100,11 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
var type = binding.getType(el);
if (type) id = id + ":" + type;
var opts = { immediate: !allowDeferred, binding: binding, el: el };
var opts = {
priority: allowDeferred ? "deferred" : "immediate",
binding: binding,
el: el
};
inputs.setInput(id, value, opts);
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -101,7 +101,7 @@ function initShiny() {
inputs = new InputValidateDecorator(inputs);
exports.onInputChange = function(name, value, opts) {
exports.setInputValue = exports.onInputChange = function(name, value, opts) {
opts = addDefaultInputOpts(opts);
inputs.setInput(name, value, opts);
};
@@ -116,7 +116,11 @@ function initShiny() {
if (type)
id = id + ":" + type;
let opts = { immediate: !allowDeferred, binding: binding, el: el };
let opts = {
priority: allowDeferred ? "deferred" : "immediate",
binding: binding,
el: el
};
inputs.setInput(id, value, opts);
}
}

View File

@@ -193,7 +193,7 @@ var InputBatchSender = function(shinyapp) {
this.pendingData[name] = value;
if (!this.reentrant) {
if (opts.immediate) {
if (opts.priority === "event") {
this.$sendNow();
} else if (!this.timerId) {
this.timerId = setTimeout(this.$sendNow.bind(this), 0);
@@ -231,7 +231,7 @@ var InputNoResendDecorator = function(target, initialValues) {
const { name: inputName, inputType: inputType } = splitInputNameType(name);
const jsonValue = JSON.stringify(value);
if (!opts.immediate &&
if (opts.priority !== "event" &&
this.lastSentValues[inputName] &&
this.lastSentValues[inputName].jsonValue === jsonValue &&
this.lastSentValues[inputName].inputType === inputType) {
@@ -276,7 +276,7 @@ var InputEventDecorator = function(target) {
evt.value = value;
evt.binding = opts.binding;
evt.el = opts.el;
evt.immediate = opts.immediate;
evt.priority = opts.priority;
$(document).trigger(evt);
@@ -284,9 +284,9 @@ var InputEventDecorator = function(target) {
name = evt.name;
if (evt.inputType !== '') name += ':' + evt.inputType;
// opts aren't passed along to lower levels in the input decorator
// Most opts aren't passed along to lower levels in the input decorator
// stack.
this.target.setInput(name, evt.value, { immediate: opts.immediate });
this.target.setInput(name, evt.value, { priority: opts.priority });
}
};
}).call(InputEventDecorator.prototype);
@@ -300,7 +300,7 @@ var InputRateDecorator = function(target) {
this.setInput = function(name, value, opts) {
this.$ensureInit(name);
if (opts.immediate)
if (opts.priority !== "deferred")
this.inputRatePolicies[name].immediateCall(name, value, opts);
else
this.inputRatePolicies[name].normalCall(name, value, opts);
@@ -365,11 +365,25 @@ const InputValidateDecorator = function(target) {
// Merge opts with defaults, and return a new object.
function addDefaultInputOpts(opts) {
return $.extend({
immediate: false,
opts = $.extend({
priority: "immediate",
binding: null,
el: null
}, opts);
if (opts && typeof(opts.priority) !== "undefined") {
switch (opts.priority) {
case "deferred":
case "immediate":
case "event":
break;
default:
throw new Error("Unexpected input value mode: '" + opts.priority + "'");
}
}
return opts;
}