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

Commit 6dd8f17

Browse files
committed
feat($compile): Allow ES6 classes as controllers with bindToController: true
Modify `$injector.invoke` so ES6 classes would be invoked using `new` Closes: #13510
1 parent 6cdbda7 commit 6dd8f17

File tree

3 files changed

+63
-5
lines changed

3 files changed

+63
-5
lines changed

src/auto/injector.js

+15-4
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,10 @@ function createInjector(modulesToLoad, strictDi) {
821821
return args;
822822
}
823823

824+
function isClass(func) {
825+
return typeof func === 'function'
826+
&& /^class\s/.test(Function.prototype.toString.call(func));
827+
}
824828

825829
function invoke(fn, self, locals, serviceName) {
826830
if (typeof locals === 'string') {
@@ -833,9 +837,16 @@ function createInjector(modulesToLoad, strictDi) {
833837
fn = fn[fn.length - 1];
834838
}
835839

836-
// http://jsperf.com/angularjs-invoke-apply-vs-switch
837-
// #5388
838-
return fn.apply(self, args);
840+
if (!isClass(fn)) {
841+
// http://jsperf.com/angularjs-invoke-apply-vs-switch
842+
// #5388
843+
return fn.apply(self, args);
844+
} else {
845+
args.unshift(null);
846+
/*jshint -W058 */ // Applying a constructor without immediate parentheses is the point here.
847+
return new (Function.prototype.bind.apply(fn, args));
848+
/*jshint +W058 */
849+
}
839850
}
840851

841852

@@ -845,7 +856,7 @@ function createInjector(modulesToLoad, strictDi) {
845856
var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
846857
var args = injectionArgs(Type, locals, serviceName);
847858
// Empty object at position 0 is ignored for invocation with `new`, but required.
848-
args.unshift({});
859+
args.unshift(null);
849860
/*jshint -W058 */ // Applying a constructor without immediate parentheses is the point here.
850861
return new (Function.prototype.bind.apply(ctor, args));
851862
/*jshint +W058 */

src/ng/compile.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@
214214
* #### `bindToController`
215215
* When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
216216
* allow a component to have its properties bound to the controller, rather than to scope. When the controller
217-
* is instantiated, the initial values of the isolate scope bindings are already available.
217+
* is instantiated, the initial values of the isolate scope bindings will be available if the controller is not an ES6 class.
218218
*
219219
* #### `controller`
220220
* Controller constructor function. The controller is instantiated before the

test/ng/compileSpec.js

+47
Original file line numberDiff line numberDiff line change
@@ -4235,6 +4235,53 @@ describe('$compile', function() {
42354235
});
42364236

42374237

4238+
it('should eventually expose isolate scope variables on ES6 class controller with controllerAs when bindToController is true', function() {
4239+
if (!/chrome/i.test(navigator.userAgent)) return;
4240+
/*jshint -W061 */
4241+
var controllerCalled = false;
4242+
module(function($compileProvider) {
4243+
$compileProvider.directive('fooDir', valueFn({
4244+
template: '<p>isolate</p>',
4245+
scope: {
4246+
'data': '=dirData',
4247+
'str': '@dirStr',
4248+
'fn': '&dirFn'
4249+
},
4250+
controller: eval(
4251+
"class Foo {" +
4252+
" constructor($scope) {}" +
4253+
" check() {" +
4254+
" expect(this.data).toEqualData({" +
4255+
" 'foo': 'bar'," +
4256+
" 'baz': 'biz'" +
4257+
" });" +
4258+
" expect(this.str).toBe('Hello, world!');" +
4259+
" expect(this.fn()).toBe('called!');" +
4260+
" controllerCalled = true;" +
4261+
" }" +
4262+
"}"
4263+
),
4264+
controllerAs: 'test',
4265+
bindToController: true
4266+
}));
4267+
});
4268+
inject(function($compile, $rootScope) {
4269+
$rootScope.fn = valueFn('called!');
4270+
$rootScope.whom = 'world';
4271+
$rootScope.remoteData = {
4272+
'foo': 'bar',
4273+
'baz': 'biz'
4274+
};
4275+
element = $compile('<div foo-dir dir-data="remoteData" ' +
4276+
'dir-str="Hello, {{whom}}!" ' +
4277+
'dir-fn="fn()"></div>')($rootScope);
4278+
element.data('$fooDirController').check();
4279+
expect(controllerCalled).toBe(true);
4280+
});
4281+
/*jshint +W061 */
4282+
});
4283+
4284+
42384285
it('should update @-bindings on controller when bindToController and attribute change observed', function() {
42394286
module(function($compileProvider) {
42404287
$compileProvider.directive('atBinding', valueFn({

0 commit comments

Comments
 (0)