Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(upgrade): Support ng-model in downgraded components #10578

Merged
merged 1 commit into from
Jan 23, 2017

Conversation

kseamon
Copy link
Contributor

@kseamon kseamon commented Aug 8, 2016

What kind of change does this PR introduce? (check one with "x")

[ ] Bugfix
[x ] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Other... Please describe:

What is the current behavior? (You can also link to an open issue here)
No form of ngModel is supported. Components that require it, such as MdInput are not usable via ngDowngrade.

What is the new behavior?
The AngularJS ng-model can now bind to Components that implement the ControlValueAccessor interface.

Does this PR introduce a breaking change? (check one with "x")

[ ] Yes
[x] No

If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...

Other information:

@kseamon
Copy link
Contributor Author

kseamon commented Aug 9, 2016

Update: ran gulp lint

@naomiblack
Copy link
Contributor

holding this PR until after RC6 settles

@kseamon
Copy link
Contributor Author

kseamon commented Oct 11, 2016

Resolved conflicts.

@gkalpak
Copy link
Member

gkalpak commented Dec 19, 2016

It might be just me, but I feel this crosses the "too much magic under the hood" boundary. ngUpgrade already performs a significant amount of magic under the hood, so I would rather not add to it, if not necessary.

I do understand that ngModel/ControlValueAccessor are very common interfaces, though, and there should be an ergonomic way to use them in upgraded/downgraded components.

I haven't thought this through, rather thinking out loud:

  • Would this be possible as an independent directive (i.e. not part of DowngradedNg2Component). It could be even part of ngUpgrade.

@petebacondarwin
Copy link
Member

I have a similar feeling, although I am not sure we could manage it through an Angular 1 directive since it would not have access to or knowledge of the Angular 2 downgraded component.

In any case I think it would be important to make this a bit more general, perhaps some way of exposing controllers to downgraded components? If that is not already possible?

@kseamon
Copy link
Contributor Author

kseamon commented Dec 19, 2016

A generalizable way of exposing controllers would be nice, but you'd then need ng1 directives that are able to work with ng2 controllers. That's probably an ok requirement for user-generated controllers, but ng-model is so fundamental to Angular that excluding it leaves out a huge percentage of otherwise viable components.

It would be good to look at a solution going the other way as well - allowing upgraded ng1 components to interoperate with ng2's NgModel as well.

@petebacondarwin
Copy link
Member

Actually the more I look at this PR the more I think it is reasonable, given the status of ngModel in the framework.

@kseamon
Copy link
Contributor Author

kseamon commented Jan 11, 2017

I'll have time to get this updated next week - would you like me to take a look at this feature for upgraded components as well?

@petebacondarwin
Copy link
Member

@kseamon - yes that would be great. Also we need it to work for upgrade/static too.

@kseamon kseamon force-pushed the ng-upgrade-ng-model branch 2 times, most recently from 9e62b98 to 2d0fb3f Compare January 19, 2017 15:43
@kseamon
Copy link
Contributor Author

kseamon commented Jan 19, 2017

Synced up with master.

@kseamon
Copy link
Contributor Author

kseamon commented Jan 19, 2017

Looks like I need to more or less copy these changes into the aot subdirectory as well?

@petebacondarwin
Copy link
Member

petebacondarwin commented Jan 19, 2017 via email

@kseamon
Copy link
Contributor Author

kseamon commented Jan 19, 2017

Added in the aot stuff. Unit tests are passing for me but there seems to have been a timeout or tool failure in travis for ios 7. Any way to rerun the tests to see if it is a fluke?

@petebacondarwin
Copy link
Member

I can rerun them...

@kseamon
Copy link
Contributor Author

kseamon commented Jan 19, 2017

One other note: as I'm diving into this again to try to figure out making this work the other way (for upgrading ng1 directives) I noticed that both ng1 and ng2 have hooks for "touched" (which is really blurred - not a touch event). Probably worth adding it, but not going to bother for this PR.

@petebacondarwin
Copy link
Member

Seems to pass now. I'll review tomorrow.

Copy link
Member

@petebacondarwin petebacondarwin left a comment

Choose a reason for hiding this comment

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

Nothing but minor refactoring that could be done here or in a subsequent PR.

@@ -47,6 +47,13 @@ export class DowngradeComponentAdapter {
childInjector, [[this.contentInsertionPoint]], this.element[0]);
this.changeDetector = this.componentRef.changeDetectorRef;
this.component = this.componentRef.instance;

if (this.ngModel && typeof this.component.writeValue === 'function' &&
typeof this.component.registerOnChange === 'function') {
Copy link
Member

Choose a reason for hiding this comment

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

I kind of feel like I would like this test to be factored out so that it is clear what we are checking for. E.g. implementsControlValueAccessor(this.component) or similar.

this.ngModel.$render = () => { this.component.writeValue(this.ngModel.$viewValue); };

this.component.registerOnChange(this.ngModel.$setViewValue.bind(this.ngModel));
}
Copy link
Member

Choose a reason for hiding this comment

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

Although I said we would need copy duplicate a lot of the code we could actually factor out this whole section of code into a function that is shared between dynamic and static versions. Perhaps put it in https://github.com/angular/angular/blob/master/modules/%40angular/upgrade/src/util.ts?

@@ -138,6 +138,9 @@ export class UpgradeAdapter {
* 2. Even thought the component is instantiated in Angular 1, it will be using Angular 2+
* syntax. This has to be done, this way because we must follow Angular 2+ components do not
* declare how the attributes should be interpreted.
* 3. ng-model is controlled by AngularJS v1 and communicates with the downgraded Ng2 component
* by way of the ControlValueAccessor interface from @angular/forms. Only components that
* implement this interface are elligible.
Copy link
Member

Choose a reason for hiding this comment

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

eligible

this._onChangeCallback(newValue);
}
}
let ng2Instance: Ng2;
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 this declaration should go above the class, since let is not hoisted.

@petebacondarwin
Copy link
Member

@kseamon thanks for this. Let me know if you want to make the changes I suggested or just merge this as-is.

@kseamon
Copy link
Contributor Author

kseamon commented Jan 20, 2017

These changes are pretty straightforward so I'll take care of them now.

That will also give us some time to discuss where I'm at with upgraded components - I'll start that conversation in slack now.

@kseamon
Copy link
Contributor Author

kseamon commented Jan 20, 2017

Suggested changes have been applied.

@petebacondarwin petebacondarwin added the action: merge The PR is ready for merge by the caretaker label Jan 20, 2017
@alxhub alxhub merged commit e21e9c5 into angular:master Jan 23, 2017
juleskremer pushed a commit to juleskremer/angular that referenced this pull request Aug 28, 2017
@sharikovvladislav
Copy link

sharikovvladislav commented Mar 29, 2018

@kseamon thank you. +100500 for you karma 🎉 🎉 🙌

This feature solves problem of syncing Angular component state with AngularJS NgFormController.

Does angular have some mechanism for setting validity of NgFormController AngularJS form accordingly to validity of component itself?
I mean: if control in Angular is not valid and it is downgraded component and it is inside of ngForm directive - it does not influence NgFormController in AngularJS.

I can't find anything at the moment.

If it is not, I am sure is not hard to add this. Something like:

if (typeof component.registerSetValidity === 'function') {
  component.registerSetValidity(ngModel.$setTouched.bind(ngModel));
}

What do you think about this?

@kseamon
@alxhub

@sharikovvladislav
Copy link

I created #23085.

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 13, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants