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

refactor($compile): remove preAssignBindingsEnabled leftovers #16580

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
30 changes: 4 additions & 26 deletions src/auto/injector.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,8 @@ var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var $injectorMinErr = minErr('$injector');

function stringifyFn(fn) {
return Function.prototype.toString.call(fn);
}

function extractArgs(fn) {
var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''),
var fnText = Function.prototype.toString.call(fn).replace(STRIP_COMMENTS, ''),
args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
return args;
}
Expand Down Expand Up @@ -914,19 +910,6 @@ function createInjector(modulesToLoad, strictDi) {
return args;
}

function isClass(func) {
// Support: IE 9-11 only
// IE 9-11 do not support classes and IE9 leaks with the code below.
if (msie || typeof func !== 'function') {
return false;
}
var result = func.$$ngIsClass;
if (!isBoolean(result)) {
result = func.$$ngIsClass = /^class\b/.test(stringifyFn(func));
}
return result;
}

function invoke(fn, self, locals, serviceName) {
if (typeof locals === 'string') {
serviceName = locals;
Expand All @@ -938,14 +921,9 @@ function createInjector(modulesToLoad, strictDi) {
fn = fn[fn.length - 1];
}

if (!isClass(fn)) {
// http://jsperf.com/angularjs-invoke-apply-vs-switch
// #5388
return fn.apply(self, args);
} else {
args.unshift(null);
return new (Function.prototype.bind.apply(fn, args))();
}
// http://jsperf.com/angularjs-invoke-apply-vs-switch
// #5388
return fn.apply(self, args);
}


Expand Down
130 changes: 57 additions & 73 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -2788,10 +2788,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
};
}

if (controllerDirectives) {
elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
}

if (newIsolateScopeDirective) {
// Initialize isolate scope bindings for new isolate scope directive.
compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
Expand All @@ -2807,53 +2803,69 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
}

// Initialize bindToController bindings
for (var name in elementControllers) {
var controllerDirective = controllerDirectives[name];
var controller = elementControllers[name];
var bindings = controllerDirective.$$bindings.bindToController;
if (controllerDirectives) {
elementControllers = createMap();
for (var name in controllerDirectives) {
var directive = controllerDirectives[name];
var locals = {
$scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
$element: $element,
$attrs: attrs,
$transclude: transcludeFn
};

controller.instance = controller();
$element.data('$' + controllerDirective.name + 'Controller', controller.instance);
controller.bindingInfo =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
}
var controllerConstructor = directive.controller;
if (controllerConstructor === '@') {
controllerConstructor = attrs[name];
}

// Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
forEach(controllerDirectives, function(controllerDirective, name) {
var require = controllerDirective.require;
if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
var instance = $controller(controllerConstructor, locals, directive.controllerAs);

$element.data('$' + name + 'Controller', instance);

// Initialize bindToController bindings
var bindings = directive.$$bindings.bindToController;
var bindingInfo = initializeDirectiveBindings(controllerScope, attrs, instance, bindings, directive);

elementControllers[name] = { instance: instance, bindingInfo: bindingInfo };
}
});

// Handle the init and destroy lifecycle hooks on all controllers that have them
forEach(elementControllers, function(controller) {
var controllerInstance = controller.instance;
if (isFunction(controllerInstance.$onChanges)) {
try {
controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
} catch (e) {
$exceptionHandler(e);
// Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
forEach(controllerDirectives, function(controllerDirective, name) {
var require = controllerDirective.require;
if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
}
}
if (isFunction(controllerInstance.$onInit)) {
try {
controllerInstance.$onInit();
} catch (e) {
$exceptionHandler(e);
});

// Handle the init and destroy lifecycle hooks on all controllers that have them
forEach(elementControllers, function(controller) {
var controllerInstance = controller.instance;
if (isFunction(controllerInstance.$onChanges)) {
try {
controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
} catch (e) {
$exceptionHandler(e);
}
}
}
if (isFunction(controllerInstance.$doCheck)) {
controllerScope.$watch(function() { controllerInstance.$doCheck(); });
controllerInstance.$doCheck();
}
if (isFunction(controllerInstance.$onDestroy)) {
controllerScope.$on('$destroy', function callOnDestroyHook() {
controllerInstance.$onDestroy();
});
}
});
if (isFunction(controllerInstance.$onInit)) {
try {
controllerInstance.$onInit();
} catch (e) {
$exceptionHandler(e);
}
}
if (isFunction(controllerInstance.$doCheck)) {
controllerScope.$watch(function() { controllerInstance.$doCheck(); });
controllerInstance.$doCheck();
}
if (isFunction(controllerInstance.$onDestroy)) {
controllerScope.$on('$destroy', function callOnDestroyHook() {
controllerInstance.$onDestroy();
});
}
});
}

// PRELINKING
for (i = 0, ii = preLinkFns.length; i < ii; i++) {
Expand Down Expand Up @@ -2981,34 +2993,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return value || null;
}

function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
var elementControllers = createMap();
for (var controllerKey in controllerDirectives) {
var directive = controllerDirectives[controllerKey];
var locals = {
$scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
$element: $element,
$attrs: attrs,
$transclude: transcludeFn
};

var controller = directive.controller;
if (controller === '@') {
controller = attrs[directive.name];
}

var controllerInstance = $controller(controller, locals, true, directive.controllerAs);

// For directives with element transclusion the element is a comment.
// In this case .data will not attach any data.
// Instead, we save the controllers for the element in a local hash and attach to .data
// later, once we have the actual element.
elementControllers[directive.name] = controllerInstance;
$element.data('$' + directive.name + 'Controller', controllerInstance.instance);
}
return elementControllers;
}

// Depending upon the context in which a directive finds itself it might need to have a new isolated
// or child scope created. For instance:
// * if the directive has been pulled into a template because another directive with a higher priority
Expand Down
42 changes: 1 addition & 41 deletions src/ng/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,11 @@ function $ControllerProvider() {
* It's just a simple call to {@link auto.$injector $injector}, but extracted into
* a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
*/
return function $controller(expression, locals, later, ident) {
return function $controller(expression, locals, ident) {
// PRIVATE API:
// param `later` --- indicates that the controller's constructor is invoked at a later time.
// If true, $controller will allocate the object with the correct
// prototype chain, but will not invoke the controller until a returned
// callback is invoked.
// param `ident` --- An optional label which overrides the label parsed from the controller
// expression, if any.
var instance, match, constructor, identifier;
later = later === true;
if (ident && isString(ident)) {
identifier = ident;
}
Expand All @@ -116,41 +111,6 @@ function $ControllerProvider() {
assertArgFn(expression, constructor, true);
}

if (later) {
// Instantiate controller later:
// This machinery is used to create an instance of the object before calling the
// controller's constructor itself.
//
// This allows properties to be added to the controller before the constructor is
// invoked. Primarily, this is used for isolate scope bindings in $compile.
//
// This feature is not intended for use by applications, and is thus not documented
// publicly.
// Object creation: http://jsperf.com/create-constructor/2
var controllerPrototype = (isArray(expression) ?
expression[expression.length - 1] : expression).prototype;
instance = Object.create(controllerPrototype || null);

if (identifier) {
addIdentifier(locals, identifier, instance, constructor || expression.name);
}

return extend(function $controllerInit() {
var result = $injector.invoke(expression, instance, locals, constructor);
if (result !== instance && (isObject(result) || isFunction(result))) {
instance = result;
if (identifier) {
// If result changed, re-assign controllerAs value to scope.
addIdentifier(locals, identifier, instance, constructor || expression.name);
}
}
return instance;
}, {
instance: instance,
identifier: identifier
});
}

instance = $injector.instantiate(expression, locals, constructor);

if (identifier) {
Expand Down
13 changes: 5 additions & 8 deletions src/ngMock/angular-mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -2332,14 +2332,11 @@ angular.mock.$RootElementProvider = function() {
*/
function createControllerDecorator() {
angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
return function(expression, locals, later, ident) {
if (later && typeof later === 'object') {
var instantiate = $delegate(expression, locals, true, ident);
var instance = instantiate();
angular.extend(instance, later);
return instance;
}
return $delegate(expression, locals, later, ident);
return function(expression, locals, bindings, ident) {
if (angular.isString(bindings)) ident = bindings;
var instance = $delegate(expression, locals, ident);
angular.extend(instance, bindings);
return instance;
};
}];

Expand Down
19 changes: 0 additions & 19 deletions test/auto/injectorSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -482,25 +482,6 @@ describe('injector', function() {
expect(instance).toEqual(new Clazz('a-value'));
expect(instance.aVal()).toEqual('a-value');
});

they('should detect ES6 classes regardless of whitespace/comments ($prop)', [
'class Test {}',
'class Test{}',
'class //<--ES6 stuff\nTest {}',
'class//<--ES6 stuff\nTest {}',
'class {}',
'class{}',
'class //<--ES6 stuff\n {}',
'class//<--ES6 stuff\n {}',
'class/* Test */{}',
'class /* Test */ {}'
], function(classDefinition) {
// eslint-disable-next-line no-eval
var Clazz = eval('(' + classDefinition + ')');
var instance = injector.invoke(Clazz);

expect(instance).toEqual(jasmine.any(Clazz));
});
}
});

Expand Down