diff --git a/.travis.yml b/.travis.yml index 1f64779b8ec..a006df6e55f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,7 @@ branches: jobs: include: + - env: "NG_VERSION=1.5" - env: "NG_VERSION=1.6" - env: "NG_VERSION=1.7" - env: "NG_VERSION=snapshot" diff --git a/package-lock.json b/package-lock.json index ab547b5abab..d5cd0fbf92b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -137,51 +137,51 @@ "dev": true }, "angular": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/angular/-/angular-1.6.8.tgz", - "integrity": "sha512-9WErZIOw1Cu1V5Yxdvxz/6YpND8ntdP71fdPpufPFJvZodZXqCjQBYrHqEoMZreO5i84O3D/Jw/vepoFt68Azw==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/angular/-/angular-1.7.2.tgz", + "integrity": "sha512-JcKKJbBdybUsmQ6x1M3xWyTYQ/ioVKJhSByEAjqrhmlOfvMFdhfMqAx5KIo8rLGk4DFolYPcCSgssjgTVjCtRQ==", "dev": true }, "angular-animate": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.6.8.tgz", - "integrity": "sha512-zKaq9vtJ+QPV6q159mJkb6uKa3SiTe0PXj+W9WO3cDhLly8LuQHYRteAAD3/SqlS7GMq2SFlJvs7VQduPJpUQw==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.7.2.tgz", + "integrity": "sha512-/MQL2FEFXhdzFUKJ9AJq6gAYz3YGsh2xHTztuQKY5FkqjEBpF5YGypgprTz153P7t51T8nFqLsG8jwkpRdM6gg==", "dev": true }, "angular-aria": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.6.8.tgz", - "integrity": "sha512-pJ24MO23zJXBmJ0X/0jkz0DtWthXTahwe4oLCrrbT6Jxtspf+FCYNTkBeC1c3fv+CNGal69NRUUMuujuRyqzeg==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.7.2.tgz", + "integrity": "sha512-tnra11eAHwwMCgWg6Uo9WKZPXF6u4NcpK8xOfYbSWKdTil4aQjUwvEJWBp6r7h312oeIF1lb6hjXOYFg0PUsYw==", "dev": true }, "angular-messages": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/angular-messages/-/angular-messages-1.6.8.tgz", - "integrity": "sha512-t8t3RA26KLScraeeE0FF9wflYO3txs+ND1A1L3nXT73bI99WjRgesNoh6eXc6206lksP+zEvGQTG1q1VU3rafQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/angular-messages/-/angular-messages-1.7.2.tgz", + "integrity": "sha512-hLBZAApHpemfTN3IsItSczrLVm+6sAIn9XRV5PEFKBmfqE1umAmPAeFByfy9DLebJyR17rcFQ5qMj/KZk0ZtBA==", "dev": true }, "angular-mocks": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.6.8.tgz", - "integrity": "sha512-yuP+PnUqOVh1ewIWAxBHOTowl/JNpTRFcVPKmIm+AZfQLU/jJxz4om4FgQY5udlIRLcbwPeXEx/TbamfC7Md9w==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.7.2.tgz", + "integrity": "sha512-yj9eWPG0usXX2eDTWM6YOmAGKraT7qHwuD+NrNyaR+mtrNr2ls77WuWXTjE1hZpmxTaGj4+R1nMY696XZn740Q==", "dev": true }, "angular-route": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/angular-route/-/angular-route-1.6.8.tgz", - "integrity": "sha512-6PlZVc9SRkzYdinL8urilbA/6RmbKdaGrgAKWo2XXgblOHLxguWcYcqYXPUdlMVbMYZuubI+OzTXMa2zol18AQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/angular-route/-/angular-route-1.7.2.tgz", + "integrity": "sha512-YshcsuhYThFbuQqWDhDvxaZtwlwV6M7dPzB2Eit215t0zx/78A7SSKGtw8SsqYyPCealrgD2QWdv4iY7kl6l1w==", "dev": true }, "angular-sanitize": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.6.8.tgz", - "integrity": "sha512-XUYxWgPwwT+DGIMzmMBMSwJbYt5g6yMiu5Pq2GIW9EuO+PBNhL4xAf8qU7b2HfH/PwM4VY5NpIa25EoKDdgXAA==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.7.2.tgz", + "integrity": "sha512-8XmhwhT+TwnLzz4VMq9kIqj9b2aBt4lgYWRw9s+Q8ysx0z/glDcnoPxMrlR/dXItlww8NM/1pscgO1bGVGdiXA==", "dev": true }, "angular-touch": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/angular-touch/-/angular-touch-1.6.8.tgz", - "integrity": "sha512-zRHaNUQu6GMWqWVJm6edfKh722iQp8gyQStvDyXpY1/J7tmHvoMaPYk4gBbGATtXX+iGs4DWlDcVKs1V6SohiQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/angular-touch/-/angular-touch-1.7.2.tgz", + "integrity": "sha512-xLuuc7Vo0XBsyMG0iAws+D3f5RuneKTqrW0ZxZ0TF2PuZOfQy/ND5HoXb3tP10pbcrE15CHV/Y6nWGLDHOv+pw==", "dev": true }, "angularytics": { diff --git a/package.json b/package.json index bdc339e20bc..9eddad5971f 100644 --- a/package.json +++ b/package.json @@ -21,14 +21,14 @@ "scss": "./dist/angular-material.scss", "devDependencies": { "add-stream": "^1.0.0", - "angular": "^1.5.0", - "angular-animate": "^1.5.0", - "angular-aria": "^1.5.0", - "angular-messages": "^1.5.0", - "angular-mocks": "^1.5.0", - "angular-route": "^1.5.0", - "angular-sanitize": "^1.5.0", - "angular-touch": "^1.5.0", + "angular": "^1.7.2", + "angular-animate": "^1.7.2", + "angular-aria": "^1.7.2", + "angular-messages": "^1.7.2", + "angular-mocks": "^1.7.2", + "angular-route": "^1.7.2", + "angular-sanitize": "^1.7.2", + "angular-touch": "^1.7.2", "angularytics": "^0.4.0", "canonical-path": "0.0.2", "cli-color": "^1.0.0", diff --git a/src/core/services/compiler/compiler.js b/src/core/services/compiler/compiler.js index 49d457d0d14..69fdeadaf27 100644 --- a/src/core/services/compiler/compiler.js +++ b/src/core/services/compiler/compiler.js @@ -102,9 +102,9 @@ function MdCompilerProvider($compileProvider) { * @name $mdCompilerProvider#respectPreAssignBindingsEnabled * * @param {boolean=} respected update the `respectPreAssignBindingsEnabled` state if provided, - * otherwise just return the current Material `respectPreAssignBindingsEnabled` state. - * @returns {boolean|MdCompilerProvider} current value if used as getter or itself (chaining) - * if used as setter + * otherwise just return the current Material `respectPreAssignBindingsEnabled` state. + * @returns {boolean|MdCompilerProvider} current value, if used as a getter, or itself (chaining) + * if used as a setter. * * @description * Call this method to enable/disable whether Material-specific (dialog/panel/toast/bottomsheet) @@ -114,26 +114,36 @@ function MdCompilerProvider($compileProvider) { * * If disabled (`false`), 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. + * The ability to disable this settings is **deprecated** and will be removed in + * AngularJS Material 1.2.0. * * If enabled (`true`) the behavior depends on the AngularJS version used: * - * - `<1.5.10` - bindings are pre-assigned. - * - `>=1.5.10 <1.7` - behaves like set to whatever `$compileProvider.preAssignBindingsEnabled()` reports. - * If the `preAssignBindingsEnabled` flag wasn't set manually, it defaults to pre-assigning bindings - * with AngularJS `1.5.x` and to calling the constructor first with AngularJS `1.6.x`. - * - `>=1.7` - the compiler calls the constructor first before assigning bindings and + * - `<1.5.10` + * - Bindings are pre-assigned. + * - `>=1.5.10 <1.7` + * - Respects whatever `$compileProvider.preAssignBindingsEnabled()` reports. If the + * `preAssignBindingsEnabled` flag wasn't set manually, it defaults to pre-assigning bindings + * with AngularJS `1.5` and to calling the constructor first with AngularJS `1.6`. + * - `>=1.7` + * - The compiler calls the constructor first before assigning bindings and * `$compileProvider.preAssignBindingsEnabled()` no longer exists. * - * The default value is `false` but will change to `true` in AngularJS Material 1.2. + * Defaults + * - The default value is `false` in AngularJS 1.6 and earlier. + * - It is planned to fix this value to `true` and not allow the `false` value in + * AngularJS Material 1.2.0. * - * It is recommended to set this flag to `true` in AngularJS Material 1.1.x. The only reason - * it's not set that way by default is backwards compatibility. Not setting the flag to `true` - * when AngularJS' `$compileProvider.preAssignBindingsEnabled()` is set to `false` - * (i.e. default behavior in AngularJS 1.6 or newer) makes it hard to unit test + * It is recommended to set this flag to `true` when using AngularJS Material 1.1.x with + * AngularJS versions >= 1.5.10. The only reason it's not set that way by default is backwards + * compatibility. + * + * By not setting the flag to `true` when AngularJS' `$compileProvider.preAssignBindingsEnabled()` + * is set to `false` (i.e. default behavior in AngularJS 1.6 or newer), unit testing of * Material Dialog/Panel/Toast/BottomSheet controllers using the `$controller` helper - * as it always follows the `$compileProvider.preAssignBindingsEnabled()` value. + * is problematic as it always follows AngularJS' `$compileProvider.preAssignBindingsEnabled()` + * value. */ - // TODO change it to `true` in Material 1.2. var respectPreAssignBindingsEnabled = false; this.respectPreAssignBindingsEnabled = function(respected) { if (angular.isDefined(respected)) { @@ -147,11 +157,11 @@ function MdCompilerProvider($compileProvider) { /** * @private * @description - * This function returns `true` if AngularJS Material-specific (dialog/panel/toast/bottomsheet) controllers have - * bindings pre-assigned in controller constructors and `false` otherwise. + * This function returns `true` if AngularJS Material-specific (dialog/panel/toast/bottomsheet) + * controllers have bindings pre-assigned in controller constructors and `false` otherwise. * - * Note that this doesn't affect directives/components created via regular AngularJS methods which constitute most - * Material and user-created components; their behavior can be checked via + * Note that this doesn't affect directives/components created via regular AngularJS methods + * which constitute most Material and user-created components; their behavior can be checked via * `$compileProvider.preAssignBindingsEnabled()` in AngularJS `>=1.5.10 <1.7.0`. * * @returns {*} current preAssignBindingsEnabled state @@ -444,30 +454,43 @@ function MdCompilerProvider($compileProvider) { /** * Creates and instantiates a new controller with the specified options. - * @param {!Object} options Options that include the controller + * @param {!Object} options Options that include the controller function or string. * @param {!Object} injectLocals Locals to to be provided in the controller DI. * @param {!Object} locals Locals to be injected to the controller. * @returns {!Object} Created controller instance. */ MdCompilerService.prototype._createController = function(options, injectLocals, locals) { - // The third and fourth arguments to $controller are considered private and are undocumented: - // https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L86 + var ctrl; + var preAssignBindingsEnabled = getPreAssignBindingsEnabled(); + // The third argument to $controller is considered private and undocumented: + // https://github.com/angular/angular.js/blob/v1.6.10/src/ng/controller.js#L102-L109. + // TODO remove the use of this third argument in AngularJS Material 1.2.0. // Passing `true` as the third argument causes `$controller` to return a function that - // gets the controller instance instead returning of the instance directly. When the + // gets the controller instance instead of returning the instance directly. When the // controller is defined as a function, `invokeCtrl.instance` is the *same instance* as - // `invokeCtrl()`. However, then the controller is an ES6 class, `invokeCtrl.instance` is a + // `invokeCtrl()`. However, when the controller is an ES6 class, `invokeCtrl.instance` is a // *different instance* from `invokeCtrl()`. - var invokeCtrl = this.$controller(options.controller, injectLocals, true, options.controllerAs); + if (preAssignBindingsEnabled) { + var invokeCtrl = this.$controller(options.controller, injectLocals, true); - if (getPreAssignBindingsEnabled() && options.bindToController) { - angular.extend(invokeCtrl.instance, locals); - } + if (options.bindToController) { + angular.extend(invokeCtrl.instance, locals); + } - // Instantiate and initialize the specified controller. - var ctrl = invokeCtrl(); + // Use the private API callback to instantiate and initialize the specified controller. + ctrl = invokeCtrl(); + } else { + // If we don't need to pre-assign bindings, avoid using the private API third argument and + // related callback. + ctrl = this.$controller(options.controller, injectLocals); + + if (options.bindToController) { + angular.extend(ctrl, locals); + } + } - if (!getPreAssignBindingsEnabled() && options.bindToController) { - angular.extend(ctrl, locals); + if (options.controllerAs) { + injectLocals.$scope[options.controllerAs] = ctrl; } // Call the $onInit hook if it's present on the controller. diff --git a/src/core/services/compiler/compiler.spec.js b/src/core/services/compiler/compiler.spec.js index 4e35a19a7b7..df5148294d9 100644 --- a/src/core/services/compiler/compiler.spec.js +++ b/src/core/services/compiler/compiler.spec.js @@ -112,7 +112,7 @@ describe('$mdCompiler service', function() { var data = compile({ template: 'hello' }); - var scope = $rootScope.$new(); + var scope = $rootScope.$new(false); data.link(scope); expect(data.element.scope()).toBe(scope); })); @@ -127,7 +127,7 @@ describe('$mdCompiler service', function() { this.injectedOne = one; } }); - var scope = $rootScope.$new(); + var scope = $rootScope.$new(false); data.link(scope); expect(data.element.controller()).toBeTruthy(); expect(data.element.controller().injectedOne).toBe(1); @@ -143,7 +143,7 @@ describe('$mdCompiler service', function() { } }); - var scope = $rootScope.$new(); + var scope = $rootScope.$new(false); data.link(scope); expect(ctrlElement).toBe(data.element); @@ -155,7 +155,7 @@ describe('$mdCompiler service', function() { controller: function Ctrl() {}, controllerAs: 'myControllerAs' }); - var scope = $rootScope.$new(); + var scope = $rootScope.$new(false); data.link(scope); expect(scope.myControllerAs).toBe(data.element.controller()); })); @@ -164,12 +164,21 @@ describe('$mdCompiler service', function() { }); - [ + var bindingStatesToTest; + if (angular.version.major === 1 && angular.version.minor >= 6) { + bindingStatesToTest = [ {respectPreAssignBindingsEnabled: true}, {respectPreAssignBindingsEnabled: false}, - // TODO change `equivalentTo` to `true` in Material 1.2. + // TODO change `equivalentTo` to `true` in Material 1.2.0. {respectPreAssignBindingsEnabled: '"default"', equivalentTo: false} - ].forEach(function(options) { + ]; + } else if (angular.version.major === 1 && angular.version.minor < 6) { + bindingStatesToTest = [ + {respectPreAssignBindingsEnabled: false} + ]; + } + + bindingStatesToTest.forEach(function(options) { var realRespectPreAssignBindingsEnabled = options.respectPreAssignBindingsEnabled; var respectPreAssignBindingsEnabled = angular.isDefined(options.equivalentTo) ? options.equivalentTo : @@ -224,8 +233,8 @@ describe('$mdCompiler service', function() { expect(isInstantiated).toBe(true); }); - // Bindings are not preassigned only if we respect the AngularJS config and they're - // disabled there. This logic will change in Material 1.2.0. + // Bindings are not pre-assigned if we respect the AngularJS config and pre-assigning + // them is disabled there. This logic will change in AngularJS Material 1.2.0. if (respectPreAssignBindingsEnabled && !preAssignBindingsEnabledInAngularJS) { it('disabled should assign bindings after constructor', function() { var isInstantiated = false; @@ -400,7 +409,7 @@ describe('$mdCompiler service', function() { it('should preserve a previous linked scope', function() { - var scope = $rootScope.$new(); + var scope = $rootScope.$new(false); var data = compile({ contentElement: $compile('