-
Notifications
You must be signed in to change notification settings - Fork 27.4k
Native class syntax breaks invoking directive controller #14240
Comments
It works fine for me on Chrome 49 (Win 10). What browser/OS did you test this on ? |
Tested with Macbook Pro / OS X El Capitan 10.11.3 (15D21): Firefox 45 throws "Error: class constructors must be invoked with |new|" The injector has "isClass" method which returns false for native class. But, Do bindings should work differently using "real" classes opposite to transformed to ES5? If my application use this behavior to initialize controller properties, when I switch to native classes, it can stop working properly. |
OK, so this error occurs when we fail to detect that a constructor is a Class rather than a function. More specifically:
|
@gkalpak What do you think about my question? Should there be a different behavior for inserting bindings to native implementation of class and ordinary function constructor? Consider this code: class Contr {
constructor() {
// expression is a '&' binding
this.data = this.expression();
}
}
app.component(..., { controller: Contr } ); When application uses Babel, controller will be a function (ES5) and bindings will be present in constructor (injector will push them). Application works fine. But, when native class would be used, injector will know that and will not push bindings. Application breaks with TypeError - this.expression is not a function... In my opinion, it can be confusing when application starts to fall if I turn off transforming from ES6 to ES5. |
Removing the current behavior would be a big breaking change and we don't want that. Yet, accessing the properties inside the constructor is already deprecated and the recommended practice is using
I don't think there is much more we can do, other than doing our best to properly detect native classes. |
This is currently breaking our production app. Any updates or a temporary workaround? |
This bug isn't stably reproduced. If I edit something seemingly unrelated in BTW, I also found that |
I can't reproduce it in 52.0.2715.0 canary (64-bit), Windows. |
…0/51 Partially fixes angular#14240.
#14531 should take care of it (until Chrome v52 is released). |
Should this be closed? I'm seeing native classes failing in Firefox 48:
|
@jgrund, it is a bug in Firefox (see #14240 (comment)). We can't really do much 😞 |
I guess you can create classes with: (with modern-browsers, and stage-1 transpilers) class Foo {
static $inject = ['$window']
static toString() {
return 'class Foo {}';
}
constructor($window) {
this.$window = $window;
}
} |
I don't think it will work, because we are calling |
It will depend on how static $inject = ['$window'] Is propagated out On Friday, July 1, 2016, Thomas Grainger [email protected] wrote:
|
class Foo {
static $inject = ['$window']
static $$isClass = true
constructor($window) {
this.$window = $window;
}
}
(function () {
oldToString = Function.prototype.toString;
Function.prototype.toString = function toString() {
const defaultString = this::oldToString();
if (!this.$$isClass) {
return defaultString;
}
return defaultString.replace(/^function ([^(]+)\(([^)]*)\)([\S\s]*)/, 'class $1 {constructor($2)$3}');
}
})();
Function.prototype.toString.apply(Foo);
|
|
lgalfaso it's transpiled to: class Foo {...}
Foo.$inject = ['$window'] |
@graingert, it is probably not a good idea t patch But the problem with |
I just made up $$isClass you can replace it with whatever. I just created a
|
Right! I was confused, because there is actually a similar property that we attach to functions/classes, but it is called So, fair enough! It is a viable work-around until browsers catch up with the spec (but I never officially recommended it 😛) |
In the few minutes of playing around, I haven't been able to reproduce "function class MyClass" on Chrome. Can you give me some pointers? class MyClass {} |
@hashseed , iirc it doesn't happen if you |
@gkalpak that I can indeed reproduce. However, given the bug description, I was hoping for a way to get the "function class" string from within the script. Do you have an example for that? |
@hashseed, theoretically, you should be able to reproduce it with OP's demo. But it isn't reproducible on my Chrome v51.0.2704.106 any more (probably fixed). |
is there anyway of opting into the deprecation and preventing those bindings from being accessed? |
@graingert, not yet (but the plan is to provide a way to opt-out). (See here for more context.) |
@gkalpak The codepen example mentioned in the bug description still failing in FF 50.1.0 |
Looks like this is fixed in 1.6 |
@katrotz: It fails because Firefox does not stringify classes in a way that allows Angular to recognize them. A hacky work-around (in the sense that it is private and might break any time) is to put a static property on the class class Foo {}
Foo.$$ngIsClass = true; |
wouldn't it be possible to |
@TitanNano, we have decided against this, since a constructor might have side effects (so you don't want to call it twice) and there is no reliable way (or at least there was none at the time) to identify the cause of the error (e.g. each browser threw a different error etc). |
Still an issue for me with 1.6.1. |
@johannesjo: The comments above explain why it happens and how to work around. If you are running into a new issue that is not discussed above, please open anew issue (with more details). |
@gkalpak I hope that doesn't sound rude, but a 'hacky workaround' is not a real fix, wouldn't you agree? A client library should deal with browser quirks even when the browser is 'wrong'. |
@johannesjo seems like it's impossible to fix without try...catch |
@graingert I see the issue, but to me it sounds like it would be possible to fix this, but only in a very nasty unfun way (handling lots of different browser specific errors). Depending on how reliable the error handling could be (well it should be at least 99.8% percent ;)) I stil think that it might be better than to live with a possibly unreliable workaround. |
The I also believe a try...catch solution could potentially catch exceptions thrown during object construction, and thus render false positives. |
Fixes error "class constructors must be invoked with |new|" See angular/angular.js#14240
Do you want to request a feature or report a bug?
Bug
What is the current behavior?
Initializing controller created with native class syntax breaks directive compilation. Problem here is with pushing bindings from directive to self initalized controller instance before constructor is invoked, but ES2015 class syntax specification forbids using constructor without "new" operator.
Codepen example: http://codepen.io/smalluban/pen/eZBeNr?editors=1012
Console output:
What is the expected behavior?
Pushing bindings to class constructor can not be fixed. With ES5 ordinary function everything works fine, but using class syntax is not safe if you don't use transpiler(like Babel). Only way is to deprecate it and push values after class is constructed, but I have no idea if this is possible (due to fact of using this functionality).
What is the motivation / use case for changing the behavior?
There is a lot of articles where we are encourage to use class syntax for creating controllers. But using classes is danger, because application will break if you use native classes.
In my opinion using Babel to transpile class syntax is temporary. Most modern browsers already support classes, so using Angular 1 in hybrid application could be used without transpilation.
Which versions of Angular, and which browser / OS are affected by this issue? Did this work in previous versions of Angular? Please also test with the latest stable and snapshot (https://code.angularjs.org/snapshot/) versions.
Angular: 1.5.0
Browser: Every browser where class syntax is supported: Chrome 49+, Edge 13+, Firefox 45+, Safari 9+.
The text was updated successfully, but these errors were encountered: