-
Notifications
You must be signed in to change notification settings - Fork 3.4k
fix(compiler): remove dependency on AngularJS private API #11320
Conversation
531bf90
to
0f7df0a
Compare
@@ -124,7 +124,7 @@ function MdCompilerProvider($compileProvider) { | |||
* - `>=1.7` - the compiler calls the constructor first before assigning bindings and | |||
* `$compileProvider.preAssignBindingsEnabled()` no longer exists. | |||
* | |||
* The default value is `false` but will change to `true` in AngularJS Material 1.2. | |||
* The default value is `false` in to AngularJS 1.6 and earlier but `true` in AngularJS 1.7. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in to AngularJS 1.6
-> in AngularJS 1.6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the info that the default will change to true
(for all AngularJS versions) in Material 1.2 was useful as well, I think it should stay here.
// TODO change it to `true` in Material 1.2. | ||
var respectPreAssignBindingsEnabled = false; | ||
var respectPreAssignBindingsEnabled; | ||
if (angular.version.major === 1 && angular.version.minor === 7) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
=== 7
-> >= 7
.
this.respectPreAssignBindingsEnabled = function(respected) { | ||
if (!respected && angular.version.major === 1 && angular.version.minor === 7) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
=== 7
-> >= 7
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The !respected
check means you'll catch a getter as well so this API won't work at all in 1.7. The error message suggests you meant to add this error for the setter, not a getter. The getter is probably fine.
IOW, this should be moved to inside the if (angular.isDefined(respected))
block.
this.respectPreAssignBindingsEnabled = function(respected) { | ||
if (!respected && angular.version.major === 1 && angular.version.minor === 7) { | ||
throw new Error( | ||
'Disabling respectPreAssignBindingsEnabled is not supported in AngularJS 1.7.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AngularJS 1.7
-> AngularJS >= 1.7
.
angular.extend(invokeCtrl.instance, locals); | ||
} | ||
var ctrl; | ||
if (angular.version.major === 1 && angular.version.minor === 7) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
=== 7
-> >= 7
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, you need to check for 1.7.1 here. In 1.7.0 the options.controllerAs
should be the 4th argument 😒
(It is still OK if you don't support pre-assigning in 1.7.0 so that material bahaves consistently with all 1.7+ versions, but you need to call $controller
differently on 1.7.0 vs 1.7.1.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
controllerAs
is an internal parameter, isn't it? Is there a way to stop relying on it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be possible. It is only used for assigning the controller instance onto locals.$scope
. It could be done manually with injectLocals.$scope[options.controllerAs] = <instance>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems like a better way to do it.
@@ -164,12 +164,24 @@ describe('$mdCompiler service', function() { | |||
|
|||
}); | |||
|
|||
[ | |||
var bindingStatesToTest; | |||
if (angular.version.major === 1 && angular.version.minor === 7) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
=== 7
-> >= 7
.
if (angular.version.major === 1 && angular.version.minor === 7) { | ||
respectPreAssignBindingsEnabled = true; | ||
} else { | ||
respectPreAssignBindingsEnabled = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can shorten that if you want:
var respectPreAssignBindingsEnabled = angular.version.major === 1 && angular.version.minor >= 7;
2918cc9
to
a96abb5
Compare
I believe that all of the feedback has been addressed. Tests still look good against 1.7.0 and 1.7.1. PTAL. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The respectPreAssignBindingsEnabled()
docs mention:
If disabled (
false
), the compiler assigns the value of each of the bindings to the properties of the controller object before the constructor of this object is called.
This isn't true for 1.7. Maybe this should be mentioned in the docs.
Other than that LGTM 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some more comments (it's almost there for me, though!).
if (getPreAssignBindingsEnabled() && options.bindToController) { | ||
angular.extend(invokeCtrl.instance, locals); | ||
var ctrl; | ||
if ((angular.version.major === 1 && angular.version.minor === 7 && angular.version.dot >= 1) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you have to check for >= 1.7.1
now that internal API usage is completely removed (i.e. the controllerAs
parameter is no longer passed).
This can be changed to just angular.version.major === 1 && angular.version.minor >= 7
as in other places.
Am I right, @gkalpak?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right 👍
@@ -124,7 +124,8 @@ function MdCompilerProvider($compileProvider) { | |||
* - `>=1.7` - the compiler calls the constructor first before assigning bindings and | |||
* `$compileProvider.preAssignBindingsEnabled()` no longer exists. | |||
* | |||
* The default value is `false` but will change to `true` in AngularJS Material 1.2. | |||
* The default value is `false` in AngularJS 1.6 and earlier but `true` in AngularJS 1.7. | |||
* It is planned to change this to always default to `true` in AngularJS Material 1.2. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Saying that the default is true
in AngularJS 1.7 suggests it can be changed, especially that the docs nowhere say that you can't change it.
Also, AngularJS 1.7
-> AngularJS 1.7 or later
.
How about something like:
In AngularJS 1.7 or later the value is hardcoded to `true`. In AngularJS 1.6 and earlier
it's `false` by default. It is planned to change this to always default to `true` in
AngularJS Material 1.2.
@@ -164,12 +164,24 @@ describe('$mdCompiler service', function() { | |||
|
|||
}); | |||
|
|||
[ | |||
var bindingStatesToTest; | |||
if (angular.version.major === 1 && angular.version.minor >= 7) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not 100% necessary but a test checking the respectPreAssignBindingsEnabled
setter throws in 1.7 would be awesome.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gave it a shot, but I'm not sure how to test an exception in a module config
function and calling it outside of that isn't allowed (not a function).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be doable, the main problem is it's not possible to call module
(which is used in tests to access the config phase) after inject
was called at least once. You'd have to move inject
from beforeEach
of it
blocks so that at the beginning of a new it
you can still call:
module(function ($mdCompilerProvider) {
expect(function () {
$compileProvider.preAssignBindingsEnabled(false);
}).toThrow();
});
I'm not 100% sure if an error thrown during the config phase doesn't break the test infrastructure but maybe it doesn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can test that it throws with something like:
it('should throw ...', () => {
module('material.core', $mdCompilerProvider =>
$mdCompilerProvider.respectPreAssignBindingsEnabled(false));
expect(inject).toThrowError(regexThatMatchesTheErrorMessage);
});
OR
it('should throw ...', () => {
let provider;
module('material.core', $mdCompilerProvider => { provider = $mdCompilerProvider; });
inject();
expect(() => provider.respectPreAssignBindingsEnabled(false)).
toThrowError(regexThatMatchesTheErrorMessage);
});
(Note: If you don't call inject()
, config blocks will not be run.)
a96abb5
to
50a8820
Compare
This can still break an app, though. |
Right, but it's a breaking change in AngularJS 1.7, so they can keep AngularJS on 1.6.x or they can refactor their app. If they only upgrade AngularJS Material (and not AngularJS), then they shouldn't see anything break. Am I missing something here? |
This is debatable 😁 AngularJS removed There are two ways to look at things:
I think (2) would be better (aligning It is a grey area 😃 Possible scenario:
(Again, this is very open to interpretation. E.g. based on peerDependency ranges, one shouldn't be using 1.7.x yet, etc.) |
To add to that: if one used a version of Material that doesn't officially support AngularJS 1.7 and then performs a patch-level update of Material and the app breaks because pre-assigning is gone then IMO it's not technically a breaking change as they were using an unsupported configuration. That is, unless there is a version of Material 1.1.x that officially supports AngularJS 1.7.0 - then this PR would definitely be a breaking change in Material so shouldn't land in 1.1.x. EDIT: I see @gkalpak has already said something like that, I just expanded on it a little. |
It was said in a different PR, but preAssignBindings has been reverted and is now back in AngularJS 1.7.2 |
@jpike88, to be clear, preAssignBindings have not been reverted in AngularJS. I.e. AngularJS 1.7.x does not support pre-assigning bindings on components and directives that are compiled through the built-in What was reverted, were some internal, private APIs (no longer used by AngularJS itself), that are used by AngularJS Material to implement pre-assigning bindings in their |
d7beae2
to
bae289e
Compare
As @mgol mentioned, there is no current version of AngularJS Material that officially supports AngularJS 1.7.x. AngularJS Material 1.1.10 will be the first version that supports it. Our current documentation (1.1.9) for
That is the recommended configuration, but it only covers half of the options. If
So it seems like we need to support this |
Deprecating it is tricky as this means you're discouraging people from using it - and not using it means having the default of I guess you can deprecate setting it to |
@mgol good point, we can't use
With the following:
|
bae289e
to
bc06844
Compare
PR updated, description in OP updated, commit msg updated. PTAL. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One remark.
// *different instance* from `invokeCtrl()`. | ||
var invokeCtrl = this.$controller(options.controller, injectLocals, true, options.controllerAs); | ||
var invokeCtrl = this.$controller(options.controller, injectLocals, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you switch the logic to not pass the third parameter if getPreAssignBindingsEnabled() === false
? This would be safer as in the recommended usage no private API would be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call. I think it makes the 1.2.0 update more straightforward as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PTAL and let me know if I missed anything.
bc06844
to
401599c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, good job!
|
||
if (!getPreAssignBindingsEnabled() && options.bindToController) { | ||
if (!preAssignBindingsEnabled && options.bindToController) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this could be moved inside the the else
block above.
remove one of two uses of private/undocumented arguments to $controller mark setting `respectPreAssignBindingsEnabled(false)` as deprecated update documentation add AngularJS 1.5.x back to TravisCI update AngularJS devDependencies to `^1.7.2` Closes #11319
401599c
to
3ebf6cb
Compare
remove one of two uses of private/undocumented arguments to $controller mark setting `respectPreAssignBindingsEnabled(false)` as deprecated update documentation add AngularJS 1.5.x back to TravisCI update AngularJS devDependencies to `^1.7.2` Closes #11319
PR Checklist
Please check that your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
MdCompilerService._createController
uses two private and undocumented AngularJS APIs (on$controller
).Some of the docs for
respectPreAssignBindingsEnabled
is confusing or incomplete.Issue Number:
Closes #11319
What is the new behavior?
respectPreAssignBindingsEnabled(false)
as deprecated^1.7.2
Does this PR introduce a breaking change?
Other information