Permalink
Browse files

fix($parse): remove deprecated promise unwrapping

The feature has been deprecated in #4317

BREAKING CHANGE: promise unwrapping has been removed.
It has been deprecated since 1.2.0-rc.3.

It can no longer be turned on.
Two methods have been removed:
* $parseProvider.unwrapPromises
* $parseProvider.logPromiseWarnings
  • Loading branch information...
rodyhaddad committed May 22, 2014
1 parent 3f365f5 commit fa6e411da26824a5bae55f37ce7dbb859653276d
Showing with 781 additions and 1,373 deletions.
  1. +24 −220 src/ng/parse.js
  2. +757 −1,153 test/ng/parseSpec.js
View
@@ -1,8 +1,6 @@
'use strict';
var $parseMinErr = minErr('$parse');
var promiseWarningCache = {};
var promiseWarning;
// Sandboxing Angular Expressions
// ------------------------------
@@ -700,14 +698,6 @@ Parser.prototype = {
if (!o) return undefined;
v = ensureSafeObject(o[i], parser.text);
if (v && v.then && parser.options.unwrapPromises) {
p = v;
if (!('$$v' in v)) {
p.$$v = undefined;
p.then(function(val) { p.$$v = val; });
}
v = v.$$v;
}
return v;
}, {
assign: function(self, value, locals) {
@@ -835,18 +825,6 @@ function setter(obj, path, setValue, fullExp, options) {
obj[key] = propertyObj;
}
obj = propertyObj;
if (obj.then && options.unwrapPromises) {
promiseWarning(fullExp);
if (!("$$v" in obj)) {
(function(promise) {
promise.then(function(val) { promise.$$v = val; }); }
)(obj);
}
if (obj.$$v === undefined) {
obj.$$v = {};
}
obj = obj.$$v;
}
}
key = ensureSafeMemberName(element.shift(), fullExp);
obj[key] = setValue;
@@ -867,101 +845,30 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
ensureSafeMemberName(key3, fullExp);
ensureSafeMemberName(key4, fullExp);
return !options.unwrapPromises
? function cspSafeGetter(scope, locals) {
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
if (pathVal == null) return pathVal;
pathVal = pathVal[key0];
return function cspSafeGetter(scope, locals) {
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
if (!key1) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key1];
if (pathVal == null) return pathVal;
pathVal = pathVal[key0];
if (!key2) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key2];
if (!key1) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key1];
if (!key3) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key3];
if (!key2) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key2];
if (!key4) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key4];
if (!key3) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key3];
return pathVal;
}
: function cspSafePromiseEnabledGetter(scope, locals) {
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope,
promise;
if (pathVal == null) return pathVal;
pathVal = pathVal[key0];
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
}
pathVal = pathVal.$$v;
}
if (!key4) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key4];
if (!key1) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key1];
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
}
pathVal = pathVal.$$v;
}
if (!key2) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key2];
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
}
pathVal = pathVal.$$v;
}
if (!key3) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key3];
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
}
pathVal = pathVal.$$v;
}
if (!key4) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key4];
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
}
pathVal = pathVal.$$v;
}
return pathVal;
};
return pathVal;
};
}
function simpleGetterFn1(key0, fullExp) {
@@ -998,9 +905,9 @@ function getterFn(path, options, fullExp) {
// When we have only 1 or 2 tokens, use optimized special case closures.
// http://jsperf.com/angularjs-parse-getter/6
if (!options.unwrapPromises && pathKeysLength === 1) {
if (pathKeysLength === 1) {
fn = simpleGetterFn1(pathKeys[0], fullExp);
} else if (!options.unwrapPromises && pathKeysLength === 2) {
} else if (pathKeysLength === 2) {
fn = simpleGetterFn2(pathKeys[0], pathKeys[1], fullExp);
} else if (options.csp) {
if (pathKeysLength < 6) {
@@ -1028,28 +935,15 @@ function getterFn(path, options, fullExp) {
// we simply dereference 's' on any .dot notation
? 's'
// but if we are first then we check locals first, and if so read it first
: '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' +
(options.unwrapPromises
? 'if (s && s.then) {\n' +
' pw("' + fullExp.replace(/(["\r\n])/g, '\\$1') + '");\n' +
' if (!("$$v" in s)) {\n' +
' p=s;\n' +
' p.$$v = undefined;\n' +
' p.then(function(v) {p.$$v=v;});\n' +
'}\n' +
' s=s.$$v\n' +
'}\n'
: '');
: '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n';
});
code += 'return s;';
/* jshint -W054 */
var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning
var evaledFnGetter = new Function('s', 'k', code); // s=scope, k=locals
/* jshint +W054 */
evaledFnGetter.toString = valueFn(code);
fn = options.unwrapPromises ? function(scope, locals) {
return evaledFnGetter(scope, locals, promiseWarning);
} : evaledFnGetter;
fn = evaledFnGetter;
}
// Only cache the value if it's not going to mess up the cache object
@@ -1116,103 +1010,13 @@ function $ParseProvider() {
var cache = {};
var $parseOptions = {
csp: false,
unwrapPromises: false,
logPromiseWarnings: true
};
/**
* @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future.
*
* @ngdoc method
* @name $parseProvider#unwrapPromises
* @description
*
* **This feature is deprecated, see deprecation notes below for more info**
*
* If set to true (default is false), $parse will unwrap promises automatically when a promise is
* found at any part of the expression. In other words, if set to true, the expression will always
* result in a non-promise value.
*
* While the promise is unresolved, it's treated as undefined, but once resolved and fulfilled,
* the fulfillment value is used in place of the promise while evaluating the expression.
*
* **Deprecation notice**
*
* This is a feature that didn't prove to be wildly useful or popular, primarily because of the
* dichotomy between data access in templates (accessed as raw values) and controller code
* (accessed as promises).
*
* In most code we ended up resolving promises manually in controllers anyway and thus unifying
* the model access there.
*
* Other downsides of automatic promise unwrapping:
*
* - when building components it's often desirable to receive the raw promises
* - adds complexity and slows down expression evaluation
* - makes expression code pre-generation unattractive due to the amount of code that needs to be
* generated
* - makes IDE auto-completion and tool support hard
*
* **Warning Logs**
*
* If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a
* promise (to reduce the noise, each expression is logged only once). To disable this logging use
* `$parseProvider.logPromiseWarnings(false)` api.
*
*
* @param {boolean=} value New value.
* @returns {boolean|self} Returns the current setting when used as getter and self if used as
* setter.
*/
this.unwrapPromises = function(value) {
if (isDefined(value)) {
$parseOptions.unwrapPromises = !!value;
return this;
} else {
return $parseOptions.unwrapPromises;
}
};
/**
* @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future.
*
* @ngdoc method
* @name $parseProvider#logPromiseWarnings
* @description
*
* Controls whether Angular should log a warning on any encounter of a promise in an expression.
*
* The default is set to `true`.
*
* This setting applies only if `$parseProvider.unwrapPromises` setting is set to true as well.
*
* @param {boolean=} value New value.
* @returns {boolean|self} Returns the current setting when used as getter and self if used as
* setter.
*/
this.logPromiseWarnings = function(value) {
if (isDefined(value)) {
$parseOptions.logPromiseWarnings = value;
return this;
} else {
return $parseOptions.logPromiseWarnings;
}
csp: false
};
this.$get = ['$filter', '$sniffer', '$log', function($filter, $sniffer, $log) {
$parseOptions.csp = $sniffer.csp;
promiseWarning = function promiseWarningFn(fullExp) {
if (!$parseOptions.logPromiseWarnings || promiseWarningCache.hasOwnProperty(fullExp)) return;
promiseWarningCache[fullExp] = true;
$log.warn('[$parse] Promise found in the expression `' + fullExp + '`. ' +
'Automatic unwrapping of promises in Angular expressions is deprecated.');
};
return function(exp) {
var parsedExpression;
Oops, something went wrong.

0 comments on commit fa6e411

Please sign in to comment.