fix($compile): remove the preAssignBindingsEnabled flag

Closes #15782

BREAKING CHANGE:

Previously, the `$compileProvider.preAssignBindingsEnabled` flag was supported.
The flag controlled whether bindings were available inside the controller
constructor or only in the `$onInit` hook. The bindings are now no longer
available in the constructor.

To migrate your code:

1. If you haven't invoked `$compileProvider.preAssignBindingsEnabled()` you
don't have to do anything to migrate.

2. If you specified `$compileProvider.preAssignBindingsEnabled(false)`, you
can remove that statement - since AngularJS 1.6.0 this is the default so your
app should still work even in AngularJS 1.6 after such removal. Afterwards,
migrating to AngularJS 1.7.0 shouldn't require any further action.

3. If you specified `$compileProvider.preAssignBindingsEnabled(true)` you need
to first migrate your code so that the flag can be flipped to `false`. The
instructions on how to do that are available in the "Migrating from 1.5 to 1.6"
guide:
https://docs.angularjs.org/guide/migration#migrating-from-1-5-to-1-6
Afterwards, remove the `$compileProvider.preAssignBindingsEnabled(true)`
statement.
This commit is contained in:
Michał Gołębiowski
2017-03-06 22:37:39 +01:00
parent c80fa1cfe1
commit 38f8c97af7
4 changed files with 6331 additions and 6603 deletions
+4 -56
View File
@@ -1372,36 +1372,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return debugInfoEnabled;
};
/**
* @ngdoc method
* @name $compileProvider#preAssignBindingsEnabled
*
* @param {boolean=} enabled update the preAssignBindingsEnabled state if provided, otherwise just return the
* current preAssignBindingsEnabled state
* @returns {*} current value if used as getter or itself (chaining) if used as setter
*
* @kind function
*
* @description
* Call this method to enable/disable whether directive controllers are assigned bindings before
* calling the controller's constructor.
* If enabled (true), 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.
*
* If disabled (false), the compiler calls the constructor first before assigning bindings.
*
* The default value is true in AngularJS 1.5.x but will switch to false in AngularJS 1.6.x.
*/
var preAssignBindingsEnabled = false;
this.preAssignBindingsEnabled = function(enabled) {
if (isDefined(enabled)) {
preAssignBindingsEnabled = enabled;
return this;
}
return preAssignBindingsEnabled;
};
var TTL = 10;
/**
* @ngdoc method
@@ -2722,33 +2692,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var controller = elementControllers[name];
var bindings = controllerDirective.$$bindings.bindToController;
if (preAssignBindingsEnabled) {
if (bindings) {
controller.bindingInfo =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
} else {
controller.bindingInfo = {};
}
var controllerResult = controller();
if (controllerResult !== controller.instance) {
// If the controller constructor has a return value, overwrite the instance
// from setupControllers
controller.instance = controllerResult;
$element.data('$' + controllerDirective.name + 'Controller', controllerResult);
if (controller.bindingInfo.removeWatches) {
controller.bindingInfo.removeWatches();
}
controller.bindingInfo =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
}
} else {
controller.instance = controller();
$element.data('$' + controllerDirective.name + 'Controller', controller.instance);
controller.bindingInfo =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
controller.instance = controller();
$element.data('$' + controllerDirective.name + 'Controller', controller.instance);
controller.bindingInfo =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
}
}
// Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
forEach(controllerDirectives, function(controllerDirective, name) {
+2 -16
View File
@@ -2207,11 +2207,6 @@ angular.mock.$RootElementProvider = function() {
* A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing
* controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}.
*
* Depending on the value of
* {@link ng.$compileProvider#preAssignBindingsEnabled `preAssignBindingsEnabled()`}, the properties
* will be bound before or after invoking the constructor.
*
*
* ## Example
*
* ```js
@@ -2267,22 +2262,13 @@ angular.mock.$RootElementProvider = function() {
* the `bindToController` feature and simplify certain kinds of tests.
* @return {Object} Instance of given controller.
*/
function createControllerDecorator(compileProvider) {
function createControllerDecorator() {
angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
return function(expression, locals, later, ident) {
if (later && typeof later === 'object') {
var preAssignBindingsEnabled = compileProvider.preAssignBindingsEnabled();
var instantiate = $delegate(expression, locals, true, ident);
if (preAssignBindingsEnabled) {
angular.extend(instantiate.instance, later);
}
var instance = instantiate();
if (!preAssignBindingsEnabled || instance !== instantiate.instance) {
angular.extend(instance, later);
}
angular.extend(instance, later);
return instance;
}
return $delegate(expression, locals, later, ident);
+6283 -6406
View File
File diff suppressed because it is too large Load Diff
+42 -125
View File
@@ -2039,89 +2039,29 @@ describe('ngMock', function() {
describe('$controllerDecorator', function() {
describe('with `preAssignBindingsEnabled(true)`', function() {
beforeEach(module(function($compileProvider) {
$compileProvider.preAssignBindingsEnabled(true);
}));
it('should support creating controller with bindings', function() {
var called = false;
var data = [
{ name: 'derp1', id: 0 },
{ name: 'testname', id: 1 },
{ name: 'flurp', id: 2 }
];
module(function($controllerProvider) {
$controllerProvider.register('testCtrl', function() {
expect(this.data).toBe(data);
called = true;
});
});
inject(function($controller, $rootScope) {
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
expect(ctrl.data).toBe(data);
expect(called).toBe(true);
it('should support creating controller with bindings', function() {
var called = false;
var data = [
{ name: 'derp1', id: 0 },
{ name: 'testname', id: 1 },
{ name: 'flurp', id: 2 }
];
module(function($controllerProvider) {
$controllerProvider.register('testCtrl', function() {
expect(this.data).toBeUndefined();
called = true;
});
});
it('should support assigning bindings when a value is returned from the constructor',
function() {
var called = false;
var data = [
{ name: 'derp1', id: 0 },
{ name: 'testname', id: 1 },
{ name: 'flurp', id: 2 }
];
module(function($controllerProvider) {
$controllerProvider.register('testCtrl', function() {
expect(this.data).toBe(data);
called = true;
return {};
});
});
inject(function($controller, $rootScope) {
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
expect(ctrl.data).toBe(data);
expect(called).toBe(true);
});
}
);
if (/chrome/.test(window.navigator.userAgent)) {
it('should support assigning bindings to class-based controller', function() {
var called = false;
var data = [
{ name: 'derp1', id: 0 },
{ name: 'testname', id: 1 },
{ name: 'flurp', id: 2 }
];
module(function($controllerProvider) {
// eslint-disable-next-line no-eval
var TestCtrl = eval('(class { constructor() { called = true; } })');
$controllerProvider.register('testCtrl', TestCtrl);
});
inject(function($controller, $rootScope) {
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
expect(ctrl.data).toBe(data);
expect(called).toBe(true);
});
});
}
inject(function($controller, $rootScope) {
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
expect(ctrl.data).toBe(data);
expect(called).toBe(true);
});
});
describe('with `preAssignBindingsEnabled(false)`', function() {
beforeEach(module(function($compileProvider) {
$compileProvider.preAssignBindingsEnabled(false);
}));
it('should support creating controller with bindings', function() {
it('should support assigning bindings when a value is returned from the constructor',
function() {
var called = false;
var data = [
{ name: 'derp1', id: 0 },
@@ -2132,6 +2072,7 @@ describe('ngMock', function() {
$controllerProvider.register('testCtrl', function() {
expect(this.data).toBeUndefined();
called = true;
return {};
});
});
inject(function($controller, $rootScope) {
@@ -2139,54 +2080,30 @@ describe('ngMock', function() {
expect(ctrl.data).toBe(data);
expect(called).toBe(true);
});
});
it('should support assigning bindings when a value is returned from the constructor',
function() {
var called = false;
var data = [
{ name: 'derp1', id: 0 },
{ name: 'testname', id: 1 },
{ name: 'flurp', id: 2 }
];
module(function($controllerProvider) {
$controllerProvider.register('testCtrl', function() {
expect(this.data).toBeUndefined();
called = true;
return {};
});
});
inject(function($controller, $rootScope) {
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
expect(ctrl.data).toBe(data);
expect(called).toBe(true);
});
}
);
if (/chrome/.test(window.navigator.userAgent)) {
it('should support assigning bindings to class-based controller', function() {
var called = false;
var data = [
{ name: 'derp1', id: 0 },
{ name: 'testname', id: 1 },
{ name: 'flurp', id: 2 }
];
module(function($controllerProvider) {
// eslint-disable-next-line no-eval
var TestCtrl = eval('(class { constructor() { called = true; } })');
$controllerProvider.register('testCtrl', TestCtrl);
});
inject(function($controller, $rootScope) {
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
expect(ctrl.data).toBe(data);
expect(called).toBe(true);
});
});
}
});
);
if (/chrome/.test(window.navigator.userAgent)) {
it('should support assigning bindings to class-based controller', function() {
var called = false;
var data = [
{ name: 'derp1', id: 0 },
{ name: 'testname', id: 1 },
{ name: 'flurp', id: 2 }
];
module(function($controllerProvider) {
// eslint-disable-next-line no-eval
var TestCtrl = eval('(class { constructor() { called = true; } })');
$controllerProvider.register('testCtrl', TestCtrl);
});
inject(function($controller, $rootScope) {
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
expect(ctrl.data).toBe(data);
expect(called).toBe(true);
});
});
}
});