feat($compile): Allow ES6 classes as controllers with bindToController: true
Modify `$injector.invoke` so ES6 classes would be invoked using `new` Closes: #13510 Closes: #13540 Closes: #13682
This commit is contained in:
+19
-4
@@ -821,6 +821,14 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
return args;
|
||||
}
|
||||
|
||||
function isClass(func) {
|
||||
// IE 9-11 do not support classes and IE9 leaks with the code below.
|
||||
if (msie <= 11) {
|
||||
return false;
|
||||
}
|
||||
return typeof func === 'function'
|
||||
&& /^class\s/.test(Function.prototype.toString.call(func));
|
||||
}
|
||||
|
||||
function invoke(fn, self, locals, serviceName) {
|
||||
if (typeof locals === 'string') {
|
||||
@@ -833,9 +841,16 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
fn = fn[fn.length - 1];
|
||||
}
|
||||
|
||||
// http://jsperf.com/angularjs-invoke-apply-vs-switch
|
||||
// #5388
|
||||
return fn.apply(self, args);
|
||||
if (!isClass(fn)) {
|
||||
// http://jsperf.com/angularjs-invoke-apply-vs-switch
|
||||
// #5388
|
||||
return fn.apply(self, args);
|
||||
} else {
|
||||
args.unshift(null);
|
||||
/*jshint -W058 */ // Applying a constructor without immediate parentheses is the point here.
|
||||
return new (Function.prototype.bind.apply(fn, args));
|
||||
/*jshint +W058 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -845,7 +860,7 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
|
||||
var args = injectionArgs(Type, locals, serviceName);
|
||||
// Empty object at position 0 is ignored for invocation with `new`, but required.
|
||||
args.unshift({});
|
||||
args.unshift(null);
|
||||
/*jshint -W058 */ // Applying a constructor without immediate parentheses is the point here.
|
||||
return new (Function.prototype.bind.apply(ctor, args));
|
||||
/*jshint +W058 */
|
||||
|
||||
+1
-1
@@ -214,7 +214,7 @@
|
||||
* #### `bindToController`
|
||||
* When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
|
||||
* allow a component to have its properties bound to the controller, rather than to scope. When the controller
|
||||
* is instantiated, the initial values of the isolate scope bindings are already available.
|
||||
* is instantiated, the initial values of the isolate scope bindings will be available if the controller is not an ES6 class.
|
||||
*
|
||||
* #### `controller`
|
||||
* Controller constructor function. The controller is instantiated before the
|
||||
|
||||
@@ -4235,6 +4235,53 @@ describe('$compile', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should eventually expose isolate scope variables on ES6 class controller with controllerAs when bindToController is true', function() {
|
||||
if (!/chrome/i.test(navigator.userAgent)) return;
|
||||
/*jshint -W061 */
|
||||
var controllerCalled = false;
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.directive('fooDir', valueFn({
|
||||
template: '<p>isolate</p>',
|
||||
scope: {
|
||||
'data': '=dirData',
|
||||
'str': '@dirStr',
|
||||
'fn': '&dirFn'
|
||||
},
|
||||
controller: eval(
|
||||
"class Foo {" +
|
||||
" constructor($scope) {}" +
|
||||
" check() {" +
|
||||
" expect(this.data).toEqualData({" +
|
||||
" 'foo': 'bar'," +
|
||||
" 'baz': 'biz'" +
|
||||
" });" +
|
||||
" expect(this.str).toBe('Hello, world!');" +
|
||||
" expect(this.fn()).toBe('called!');" +
|
||||
" controllerCalled = true;" +
|
||||
" }" +
|
||||
"}"
|
||||
),
|
||||
controllerAs: 'test',
|
||||
bindToController: true
|
||||
}));
|
||||
});
|
||||
inject(function($compile, $rootScope) {
|
||||
$rootScope.fn = valueFn('called!');
|
||||
$rootScope.whom = 'world';
|
||||
$rootScope.remoteData = {
|
||||
'foo': 'bar',
|
||||
'baz': 'biz'
|
||||
};
|
||||
element = $compile('<div foo-dir dir-data="remoteData" ' +
|
||||
'dir-str="Hello, {{whom}}!" ' +
|
||||
'dir-fn="fn()"></div>')($rootScope);
|
||||
element.data('$fooDirController').check();
|
||||
expect(controllerCalled).toBe(true);
|
||||
});
|
||||
/*jshint +W061 */
|
||||
});
|
||||
|
||||
|
||||
it('should update @-bindings on controller when bindToController and attribute change observed', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.directive('atBinding', valueFn({
|
||||
|
||||
Reference in New Issue
Block a user