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

feat(compiler): respect preAssignBindingsEnabled state #10726

Merged
merged 1 commit into from
Aug 3, 2017

Conversation

mgol
Copy link
Member

@mgol mgol commented Jun 8, 2017

  1. The $mdCompiler is now able to respect the preAssignBindingsEnabled state
    when using AngularJS 1.5.10 or higher; to enable, invoke:

    $mdCompilerProvider.respectPreAssignBindingsEnabled(true);

  2. $mdCompiler now understands the $onInit lifecycle hooks in controllers.
    Note that no other AngularJS 1.5+ lifecycle hooks are supported currently.

Invoking:

$mdCompilerProvider.respectPreAssignBindingsEnabled(true);

will make bindings in Material custom components like $mdDialog or $mdToast
only available in controller constructors if they are available in constructors
of all other AngularJS controllers. By default this will happen in AngularJS
1.6 or newer. This can also be achieved with AngularJS >=1.5.10 <1.6.0 if:

$compilerProvider.preAssignBindingsEnabled(false);

is invoked.

Example:

$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.
}

Huge thanks to Paul Gschwendtner (@devversion) for the initial draft version of
this feature.

Fixes #10016
Fixed #10377
Ref #10469

NOTE: I'm marking this PR as WIP as I still want to test it manually with some real code to ensure there's nothing left to do. I also need to think if there's something else I need to test here.

@mgol mgol changed the title feat(compiler): respect preAssignBindingsEnabled state [WIP] feat(compiler): respect preAssignBindingsEnabled state Jun 8, 2017
@googlebot googlebot added the cla: yes PR author has signed Google's CLA: https://opensource.google.com/docs/cla/ label Jun 8, 2017
@mgol mgol force-pushed the no-pre-assigning branch from 1cb80f2 to acb9c98 Compare June 9, 2017 10:58
mgol added a commit to mgol/material that referenced this pull request Jun 9, 2017
1. The `$mdCompiler` is now able to respect the `preAssignBindingsEnabled` state
when using AngularJS 1.5.10 or higher; to enable, invoke:

    $mdCompilerProvider.respectPreAssignBindingsEnabled(true);

2. `$mdCompiler` now understands the `$onInit` lifecycle hooks in controllers.
Note that no other AngularJS 1.5+ lifecycle hooks are supported currently.

Invoking:

    $mdCompilerProvider.respectPreAssignBindingsEnabled(true);

will make bindings in Material custom components like `$mdDialog` or `$mdToast`
only available in controller constructors if they are available in constructors
of all other AngularJS controllers. By default this will happen in AngularJS
1.6 or newer. This can also be achieved with AngularJS >=1.5.10 <1.6.0 if:

    $compilerProvider.preAssignBindingsEnabled(false);

is invoked.

Example:

```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.
}
```

Huge thanks to Paul Gschwendtner (@devversion) for the initial draft version of
this feature.

Fixes angular#10016
Ref angular#10469
Closes angular#10726
@ThomasBurleson ThomasBurleson added the in progress Mainly for in progress PRs, but may be used for issues that require multiple PRs label Jun 10, 2017
@ThomasBurleson ThomasBurleson added this to the 1.1.5 milestone Jun 10, 2017
* This method and the option to assign the bindings before calling the controller's constructor
* will be removed in v1.3.0.
*/
var respectPreAssignBindingsEnabled = false;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a TODO comment about the default changing to true in Material 1.2.


/** @private @const {!angular.$compile} */
this.$compile = $compile;
// respectPreAssignBindingsEnabled === true
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment that we're ignoring the AngularJS setting in this case.

[
{respectPreAssignBindingsEnabled: true},
{respectPreAssignBindingsEnabled: false},
{respectPreAssignBindingsEnabled: '"default"', equivalentTo: false}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment about the Material 1.2 change.


beforeEach(function() {
module(function($mdCompilerProvider) {
console.log(angular.version, preAssignBindingsEnabledInAngularJS);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove it

if (respectPreAssignBindingsEnabled && !preAssignBindingsEnabledInAngularJS) {
it('$mdCompileProvider.preAssignBindingsEnabled should report bindings are not pre-assigned', function() {
module(function($mdCompilerProvider) {
expect($mdCompilerProvider.preAssignBindingsEnabled()).toBe(false);
Copy link
Member Author

@mgol mgol Jun 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be true; why did the tests pass? I need to check that.

EDIT: I commented in a wrong place.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should it be true?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't; I commented on a wrong line; fixed.

Nice catch!


});

[
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test asserting that if bindToController is set to false the $onInit method is called anyway.

* makes it hard to unit test Material Dialog/Toast controllers using the `$controller` helper as it always follows
* the `$compileProvider.preAssignBindingsEnabled()` value.
*
* @deprecated
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the deprecated note (here & in other places if present).

*
* This method will be removed in v1.3.0.
*/
this.preAssignBindingsEnabled = function() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we don't need this method to be public until someone needs it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, maybe change the name to indicate it's a getter? Like getPreAssignBindingsEnabled.

Copy link
Member

@gkalpak gkalpak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 💯

if (respectPreAssignBindingsEnabled && !preAssignBindingsEnabledInAngularJS) {
it('$mdCompileProvider.preAssignBindingsEnabled should report bindings are not pre-assigned', function() {
module(function($mdCompilerProvider) {
expect($mdCompilerProvider.preAssignBindingsEnabled()).toBe(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should it be true?

/** @private @const {!angular.$q} */
this.$q = $q;
MdCompilerProvider.$inject = ['$compileProvider'];
/** @this */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary?

* @name $mdCompilerProvider#respectPreAssignBindingsEnabled
*
* @param {boolean=} enabled update the respectPreAssignBindingsEnabled state if provided, otherwise just return
* the current mdPreAssignBindingsEnabled state
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mdPreAssignBindingsEnabled --> respectPreAssignBindingsEnabled

* ](https://code.angularjs.org/1.6.4/docs/api/ng/provider/$compileProvider#preAssignBindingsEnabled)
* for more information.
*
* If disabled (false), the compiler assigns the value of each of the bindings to the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disabled (false) --> enabled (true)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gkalpak - @mgol and I reviewed this PR yesterday... he is making slight improvements within the next 2 days. Standy by...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😞 OK 😞
But I'll be back 😈

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course. I expect nothing less.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gkalpak What's wrong here? I think current text is OK and your proposed change is wrong...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it seems correct now. Not sure what happened 😁

* @ngdoc method
* @name $mdCompilerProvider#respectPreAssignBindingsEnabled
*
* @param {boolean=} enabled update the respectPreAssignBindingsEnabled state if provided, otherwise just return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enabled --> respect (?)
(Or was enabled intntional?)

// For compatibility with AngularJS 1.6+, we should always use the $onInit hook in
// interimElements. The $mdCompiler simulates the $onInit hook for all versions.
this.$onInit = function() {
var isPrompt = this.$type == 'prompt';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use ===

Copy link
Member Author

@mgol mgol Jun 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is exactly the same code that was here before. I'd rather not change unrelated things in this PR.

The same applies to most of your other comments.

If you ignore whitespace-only changes (via appending ?w=1 to the URL: https://github.com/angular/material/pull/10726/files?w=1) you will only see what really changed in the logic.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah i understand.. thanks!

}

this.hide = function() {
$mdDialog.hide(isPrompt ? this.result : true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think hide returns a promise so let's return it so it can be chainable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.hide = function() {
 return $mdDialog.hide(isPrompt ? this.result : true);
}

$mdDialog.hide(isPrompt ? this.result : true);
};
this.abort = function() {
$mdDialog.cancel();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return here as well

};
this.keypress = function($event) {
if ($event.keyCode === $mdConstant.KEY_CODE.ENTER) {
$mdDialog.hide(this.result);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return here as well

and outside the if

return $q.resolve();

});

this.resolve = function() {
$mdToast.hide( ACTION_RESOLVE );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return here as well

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.resolve = function() {
   return $mdToast.hide( ACTION_RESOLVE );
}

module(function($mdCompilerProvider) {
console.log(angular.version, preAssignBindingsEnabledInAngularJS);
// Don't set the value so that the default state can be tested.
if (realRespectPreAssignBindingsEnabled === true || realRespectPreAssignBindingsEnabled === false) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cant we do angular.isDefined(realRespectPreAssignBindingsEnabled) instead?

Copy link
Member Author

@mgol mgol Jun 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why I didn't think about that having used exactly this method in source. :)

Copy link
Member

@EladBezalel EladBezalel Jun 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why it hit me

Copy link
Member

@gkalpak gkalpak Jun 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mgol: Because you want to differentiate true/false from "default". angular.isDefined() won't work here.

Copy link
Member Author

@mgol mgol Jun 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that's why I didn't think about that. 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about

typeof(realRespectPreAssignBindingsEnabled) === 'boolean'

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would work. As would realRespectPreAssignBindingsEnabled !== 'default'.

} else {
it('$mdCompileProvider.preAssignBindingsEnabled should report bindings are pre-assigned', function() {
module(function($mdCompilerProvider) {
expect($mdCompilerProvider.preAssignBindingsEnabled()).toBe(false);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be true; why did the tests pass? I need to check that.

@mgol mgol force-pushed the no-pre-assigning branch from acb9c98 to cb80f28 Compare June 21, 2017 13:08
mgol added a commit to mgol/material that referenced this pull request Jun 21, 2017
1. The `$mdCompiler` is now able to respect the `preAssignBindingsEnabled` state
when using AngularJS 1.5.10 or higher; to enable, invoke:

    $mdCompilerProvider.respectPreAssignBindingsEnabled(true);

2. `$mdCompiler` now understands the `$onInit` lifecycle hooks in controllers.
Note that no other AngularJS 1.5+ lifecycle hooks are supported currently.

Invoking:

    $mdCompilerProvider.respectPreAssignBindingsEnabled(true);

will make bindings in Material custom components like `$mdDialog` or `$mdToast`
only available in controller constructors if they are available in constructors
of all other AngularJS controllers. By default this will happen in AngularJS
1.6 or newer. This can also be achieved with AngularJS >=1.5.10 <1.6.0 if:

    $compilerProvider.preAssignBindingsEnabled(false);

is invoked.

Example:

```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.
}
```

Huge thanks to Paul Gschwendtner (@devversion) for the initial draft version of
this feature.

Fixes angular#10016
Ref angular#10469
Closes angular#10726
@mgol
Copy link
Member Author

mgol commented Jun 21, 2017

@ThomasBurleson @EladBezalel @gkalpak PR updated, comments addressed.

@mgol mgol changed the title [WIP] feat(compiler): respect preAssignBindingsEnabled state feat(compiler): respect preAssignBindingsEnabled state Jun 21, 2017
@mgol
Copy link
Member Author

mgol commented Jun 21, 2017

I've tested this with one of my company's internal apps and at first sight everything seemed to work with $mdCompilerProvider#respectPreAssignBindingsEnabled(true) (including tests with some minor modifications).

I've also run tests on default settings and everything seemed to work without any modifications.

There's one last thing to resolve - @jbedard & @gkalpak mentioned that exposing $mdCompilerProvider.preAssignBindingsEnabled() to the public is not necessary, it may be an internal function. Let me know what you think, @ThomasBurleson; I can apply such a change. The rest should be ready.

1. The `$mdCompiler` is now able to respect the `preAssignBindingsEnabled` state
when using AngularJS 1.5.10 or higher; to enable, invoke:

    $mdCompilerProvider.respectPreAssignBindingsEnabled(true);

2. `$mdCompiler` now understands the `$onInit` lifecycle hooks in controllers.
Note that no other AngularJS 1.5+ lifecycle hooks are supported currently.

Invoking:

    $mdCompilerProvider.respectPreAssignBindingsEnabled(true);

will make bindings in Material custom components like `$mdDialog` or `$mdToast`
only available in controller constructors if they are available in constructors
of all other AngularJS controllers. By default this will happen in AngularJS
1.6 or newer. This can also be achieved with AngularJS >=1.5.10 <1.6.0 if:

    $compilerProvider.preAssignBindingsEnabled(false);

is invoked.

Example:

```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.
}
```

Huge thanks to Paul Gschwendtner (@devversion) for the initial draft version of
this feature.

Fixes angular#10016
Ref angular#10469
Closes angular#10726
@mgol mgol force-pushed the no-pre-assigning branch from c8d2e49 to d4487f9 Compare June 21, 2017 16:07
Copy link
Contributor

@ThomasBurleson ThomasBurleson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@ThomasBurleson ThomasBurleson added P0 - Presubmit First and removed in progress Mainly for in progress PRs, but may be used for issues that require multiple PRs labels Jun 21, 2017
@jelbourn
Copy link
Member

jelbourn commented Aug 2, 2017

Looks like this succeeds on google presubmit

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
cla: yes PR author has signed Google's CLA: https://opensource.google.com/docs/cla/
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants