Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix($parse): remove deprecated promise unwrapping #7555

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 24 additions & 220 deletions src/ng/parse.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
'use strict';

var $parseMinErr = minErr('$parse');
var promiseWarningCache = {};
var promiseWarning;

// Sandboxing Angular Expressions
// ------------------------------
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand Down
Loading