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

feat($compile): set preAssignBindingsEnabled to false by default #15352

Closed
wants to merge 1 commit into from

Conversation

mgol
Copy link
Member

@mgol mgol commented Nov 2, 2016

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
A breaking change.

What is the current behavior? (You can also link to an open issue here)
#15350

What is the new behavior (if this is a feature change)?
No.

Does this PR introduce a breaking change?
Yes

Please check if the PR fulfills these requirements

Other information:

Fixes #15350

BREAKING CHANGE: Previously, $compileProvider.preAssignBindingsEnabled was
set to true by default. This means bindings were pre-assigned in component
constructors. In Angular 1.5+ the place to put the initialization logic
relying on bindings being present is the controller $onInit method.

To migrate follow the example below:

Before:

angular.module('myApp', [])
  .component('myComponent', {
    controller: 'MyController',
    template: '<div>{{ $ctrl.a }} + {{ $ctrl.b }} = {{ $ctrl.sum }}</div>',
    bindings: {
      a: '<',
      b: '<'
    }
  })
  .controller('MyController', function() {
    this.sum = this.a + this.b;
  });

After:

angular.module('myApp', [])
  .component('myComponent', {
    controller: 'MyController',
    template: '<div>{{ $ctrl.a }} + {{ $ctrl.b }} = {{ $ctrl.sum }}</div>',
    bindings: {
      a: '<',
      b: '<'
    }
  })
  .controller('MyController', function() {
    this.$onInit = function () {
      this.sum = this.a + this.b;
    };
  });

If you need to support both Angular 1.4 and 1.6, e.g. you're writing a library,
you need to check for Angular version and apply proper logic. Follow the
example below:

Before:

angular.module('myApp', [])
  .directive('myDirective', function() {
    return {
      controller: 'MyController',
      template: '<div>{{ $ctrl.a }} + {{ $ctrl.b }} = {{ $ctrl.sum }}</div>',
      scope: {
        a: '=',
        b: '='
      },
      bindToController: true,
      controllerAs: '$ctrl'
    };
  })
  .controller('MyController', function($scope) {
    this.sum = this.a + this.b;
  });

After:

angular.module('myApp', [])
  .directive('myDirective', function() {
    return {
      controller: 'MyController',
      template: '<div>{{ $ctrl.a }} + {{ $ctrl.b }} = {{ $ctrl.sum }}</div>',
      scope: {
        a: '=',
        b: '='
      },
      bindToController: true,
      controllerAs: '$ctrl'
    };
  })
  .controller('MyController', function($scope) {
    this.$onInit = function() {
      this.sum = this.a + this.b;
    };
    if (angular.version.major === 1 && angular.version.minor <= 4) {
      this.$onInit();
    }
  });

@petebacondarwin
Copy link
Contributor

LGTM - in the BC message 2nd example you have

  .config(function($compileProvider) {
    $compileProvider.preAssignBindingsEnabled(false);
  })

This should not be there right? It would affect all apps that use the library.
By putting logic in the $onInit function we are good whatever the setting of preAssignBindingsEnabled is.

@mgol
Copy link
Member Author

mgol commented Nov 3, 2016

@petebacondarwin That was a leftover from my tests in a plunker. Fixed.

@mgol mgol force-pushed the preAssignBindingsEnabled branch from aa3164c to 2b68f9f Compare November 3, 2016 12:08
@gkalpak
Copy link
Member

gkalpak commented Nov 3, 2016

Regarding the commit message:

  1. I think it is better for the examples to be as concise as possible. E.g.

    // Instead of:
    angular.module('myApp', [])
      .component('myComponent', {
        controller: 'MyController',
        template: '<div>{{ $ctrl.a }} + {{ $ctrl.b }} = {{ $ctrl.sum }}</div>',
        bindings: {
          a: '<',
          b: '<'
        }
      })
      .controller('MyController', function() {
        this.sum = this.a + this.b;
      });
    
    // I would prefer:
    .component('myComponent', {
      bindings: {value: '<'},
      controller: function() {
        this.doubleValue = this.value * 2;
      }
    })

Also, the section about supporting 1.4 and 1.6 feels a little out of place imo. It is not really related to this change (changing the default to true), but rather to us making it configurable in the first place. The exact same is true for 1.5.9+, because you don't know whether the app will have pre-assignment enabled or not.

It is useful info and should probably be included somewhere in the docs/migration guide, but not in the commit message imo.

@mgol
Copy link
Member Author

mgol commented Nov 3, 2016

@gkalpak I like the simplification in general; do you think it's not necessary to include a template?

I agree the library part would be better placed in a commit adding the preAssignBindingsEnabled flag; it's too late for that but I guess we can add it in a migration guide.

@mgol mgol force-pushed the preAssignBindingsEnabled branch from 2b68f9f to 3f3379f Compare November 3, 2016 12:33
Fixes angular#15350

BREAKING CHANGE: Previously, $compileProvider.preAssignBindingsEnabled was
set to true by default. This means bindings were pre-assigned in component
constructors. In Angular 1.5+ the place to put the initialization logic
relying on bindings being present is the controller $onInit method.

To migrate follow the example below:

Before:

```js
angular.module('myApp', [])
  .component('myComponent', {
    bindings: {value: '<'},
    controller: function() {
      this.doubleValue = this.value * 2;
    }
  });
```

After:
```js
angular.module('myApp', [])
  .component('myComponent', {
    bindings: {value: '<'},
    controller: function() {
      this.$onInit = function() {
        this.doubleValue = this.value * 2;
      };
    }
  });
```

If you don't have time to migrate the code at the moment, you can flip the
setting back to true:
```js
angular.module('myApp', [])
  .config(function($compileProvider) {
    $compileProvider.preAssignBindingsEnabled(false);
  })
  .component('myComponent', {
    bindings: {value: '<'},
    controller: function() {
      this.doubleValue = this.value * 2;
    }
  });
```
Don't do this if you're writing a library, though, as you shouldn't change
global configuration then.
@mgol mgol force-pushed the preAssignBindingsEnabled branch from 3f3379f to faf7d95 Compare November 3, 2016 12:33
@mgol
Copy link
Member Author

mgol commented Nov 3, 2016

@gkalpak @petebacondarwin Commit message updated.

@gkalpak
Copy link
Member

gkalpak commented Nov 3, 2016

LGTM

@petebacondarwin
Copy link
Contributor

LGTM2 - please merge - can you create an issue for the migration doc that we mention this 1.4-1.6 library thing?

@mgol
Copy link
Member Author

mgol commented Nov 3, 2016 via email

@mgol mgol closed this in bcd0d4d Nov 3, 2016
@mgol mgol deleted the preAssignBindingsEnabled branch November 3, 2016 14:24
devversion added a commit to devversion/material that referenced this pull request Mar 10, 2017
* The `$mdCompiler` now respects the `preAssignBindingsEnabled` state when using AngularJS 1.6 or higher.

BREAKING CHANGE:

Developers that are upgrading from AngularJS 1.5 to AngularJS 1.6 may run into issues with their custom controllers.

All custom controllers that are are being used inside of Material components, like in the `$mdDialog` or `$mdToast`, can be affected.

> Limited to controllers that are using in combinaton with the `bindToController` and `locals` option.

Angular Material starts respecting the [`preAssignBindingsEnabled`](angular/angular.js#15352) state when using AngularJS 1.6+

This will mean that bindings are no longer initialized at `constructor` time.

```js
$mdDialog.show({
  locals: {
    myVar: true
  },
  controller: MyController,
  bindToController: true
}

function MyController() {
  // No locals from Angular Material are set yet. e.g myVar is undefined.
}

MyController.prototype.$onInit = function() {
  // Bindings are now set in the $onInit lifecycle hook.
}
```

To provide consistency, Angular Material components with custom controllers now work with the `$onInit` lifecycle hook (**no** other lifecycle hooks)

Fixes angular#10016
devversion added a commit to devversion/material that referenced this pull request Mar 10, 2017
* The `$mdCompiler` now respects the `preAssignBindingsEnabled` state when using AngularJS 1.6 or higher.

**BREAKING CHANGE**

Developers that are upgrading from AngularJS 1.5 to AngularJS 1.6 may run into issues with their custom controllers.

All custom controllers that are are being used inside of Material components, like in the `$mdDialog` or `$mdToast`, can be affected.

> Limited to controllers that are used in combination with the `bindToController` and `locals` option.

Angular Material starts respecting the [`preAssignBindingsEnabled`](angular/angular.js#15352) state when using AngularJS 1.6+

This will mean that bindings are no longer initialized at `constructor` time.

```js
$mdDialog.show({
  locals: {
    myVar: true
  },
  controller: MyController,
  bindToController: true
}

function MyController() {
  // No locals from Angular Material are set yet. e.g myVar is undefined.
}

MyController.prototype.$onInit = function() {
  // Bindings are now set in the $onInit lifecycle hook.
}
```

To provide consistency, Angular Material components with custom controllers now work with the `$onInit` lifecycle hook (**no** other lifecycle hooks)

Fixes angular#10016
devversion added a commit to devversion/material that referenced this pull request Mar 11, 2017
* The `$mdCompiler` now respects the `preAssignBindingsEnabled` state when using AngularJS 1.6 or higher.

**BREAKING CHANGE**

Developers that are upgrading from AngularJS 1.5 to AngularJS 1.6 may run into issues with their custom controllers.

All custom controllers that are are being used inside of Material components, like in the `$mdDialog` or `$mdToast`, can be affected.

> Limited to controllers that are used in combination with the `bindToController` and `locals` option.

Angular Material starts respecting the [`preAssignBindingsEnabled`](angular/angular.js#15352) state when using AngularJS 1.6+

This will mean that bindings are no longer initialized at `constructor` time.

```js
$mdDialog.show({
  locals: {
    myVar: true
  },
  controller: MyController,
  bindToController: true
}

function MyController() {
  // No locals from Angular Material are set yet. e.g myVar is undefined.
}

MyController.prototype.$onInit = function() {
  // Bindings are now set in the $onInit lifecycle hook.
}
```

To provide consistency, Angular Material components with custom controllers now work with the `$onInit` lifecycle hook (**no** other lifecycle hooks)

Fixes angular#10016
ellimist pushed a commit to ellimist/angular.js that referenced this pull request Mar 15, 2017
Fixes angular#15350
Closes angular#15352

BREAKING CHANGE: Previously, $compileProvider.preAssignBindingsEnabled was
set to true by default. This means bindings were pre-assigned in component
constructors. In Angular 1.5+ the place to put the initialization logic
relying on bindings being present is the controller $onInit method.

To migrate follow the example below:

Before:

```js
angular.module('myApp', [])
  .component('myComponent', {
    bindings: {value: '<'},
    controller: function() {
      this.doubleValue = this.value * 2;
    }
  });
```

After:
```js
angular.module('myApp', [])
  .component('myComponent', {
    bindings: {value: '<'},
    controller: function() {
      this.$onInit = function() {
        this.doubleValue = this.value * 2;
      };
    }
  });
```

If you don't have time to migrate the code at the moment, you can flip the
setting back to true:
```js
angular.module('myApp', [])
  .config(function($compileProvider) {
    $compileProvider.preAssignBindingsEnabled(false);
  })
  .component('myComponent', {
    bindings: {value: '<'},
    controller: function() {
      this.doubleValue = this.value * 2;
    }
  });
```
Don't do this if you're writing a library, though, as you shouldn't change
global configuration then.
mgol pushed a commit to mgol/material that referenced this pull request Jun 5, 2017
* The `$mdCompiler` now respects the `preAssignBindingsEnabled` state when using AngularJS 1.6 or higher.

**BREAKING CHANGE**

Developers that are upgrading from AngularJS 1.5 to AngularJS 1.6 may run into issues with their custom controllers.

All custom controllers that are are being used inside of Material components, like in the `$mdDialog` or `$mdToast`, can be affected.

> Limited to controllers that are used in combination with the `bindToController` and `locals` option.

Angular Material starts respecting the [`preAssignBindingsEnabled`](angular/angular.js#15352) state when using AngularJS 1.6+

This will mean that bindings are no longer initialized at `constructor` time.

```js
$mdDialog.show({
  locals: {
    myVar: true
  },
  controller: MyController,
  bindToController: true
}

function MyController() {
  // No locals from Angular Material are set yet. e.g myVar is undefined.
}

MyController.prototype.$onInit = function() {
  // Bindings are now set in the $onInit lifecycle hook.
}
```

To provide consistency, Angular Material components with custom controllers now work with the `$onInit` lifecycle hook (**no** other lifecycle hooks)

Fixes angular#10016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Flip the $compileProvider.preAssignBindingsEnabled flag to false by default
4 participants