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

fix: allow context of EmbeddedViewRef to be changed and avoid mutating context in NgTemplateOutlet #40360

Closed

Conversation

crisbeto
Copy link
Member

@crisbeto crisbeto commented Jan 8, 2021

Currently NgTemplateOutlet recreates its view if its template is swapped out or a context object with a different shape is passed in. If an object with the same shape is passed in, we preserve the old view and we mutate the previous object. This mutation of the original object can be undesirable if two objects with the same shape are swapped between two different template outlets.

The current behavior is a result of a limitation in core where the context of an embedded view is read-only.

These changes are split up into two commits which aim to resolve the issue:

  1. The first commit makes EmbeddedViewRef.context writeable so that the object doesn't have to be mutated anymore and there didn't seem to be a good reason why it was read-only to begin with.
  2. The second commit resolves the context mutation issue by swapping out the context, rather than mutating it, when a new context object comes into NgTemplateOutlet.

Fixes #24515.

@google-cla google-cla bot added the cla: yes label Jan 8, 2021
@crisbeto crisbeto force-pushed the 24515/embedded-view-ref-context branch from 8f83124 to 38174ea Compare January 8, 2021 13:11
@crisbeto crisbeto marked this pull request as ready for review January 8, 2021 13:31
@pullapprove pullapprove bot requested a review from atscott January 8, 2021 13:31
@crisbeto crisbeto added action: review The PR is still awaiting reviews from at least one requested reviewer area: common Issues related to APIs in the @angular/common package area: core Issues related to the framework runtime target: patch This PR is targeted for the next patch release labels Jan 8, 2021
@ngbot ngbot bot modified the milestone: Backlog Jan 8, 2021
Copy link
Contributor

@atscott atscott left a comment

Choose a reason for hiding this comment

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

reviewed-for: fw-core
reviewed-for: public-api
reviewed-for: size-tracking

@pullapprove pullapprove bot requested a review from atscott January 11, 2021 18:04
@atscott
Copy link
Contributor

atscott commented Jan 11, 2021

presubmit

@crisbeto crisbeto added action: merge The PR is ready for merge by the caretaker action: presubmit The PR is in need of a google3 presubmit and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Jan 11, 2021
@atscott
Copy link
Contributor

atscott commented Jan 13, 2021

FYI - it looks like there might be a real failure in g3 due to this change. I haven't gotten a chance to investigate yet, but the bit of code in the template that seems to be broken is

        <ng-template [ngTemplateOutlet]="createFormTemplate"
                     [ngTemplateOutletContext]="{$implicit: (sourceBackup$ | async)}">
        </ng-template>
        <ng-template #createFormTemplate let-sourceBackup>
          <form [formGroup]="createForm"
                (ngSubmit)="create(sourceBackup)">
           ...
        </ng-template>

and then the failure is Cannot read property 'name' of undefined from the create method trying to read sourceBackup, which must be undefined. I won't get to investigating this until tomorrow, but maybe that's enough info for you to get started before then.

@atscott atscott removed the action: merge The PR is ready for merge by the caretaker label Jan 13, 2021
@crisbeto crisbeto added target: major This PR is targeted for the next major release and removed target: patch This PR is targeted for the next patch release labels Jan 14, 2021
@AndrewKushnir AndrewKushnir modified the milestones: Backlog, v12-candidates Jan 14, 2021
@AndrewKushnir AndrewKushnir removed the action: presubmit The PR is in need of a google3 presubmit label Jan 14, 2021
@crisbeto crisbeto force-pushed the 24515/embedded-view-ref-context branch from 38174ea to e6e6d67 Compare January 23, 2021 09:53
@crisbeto crisbeto changed the title fix: allow context of EmbeddedViewRef to be changed and avoid mutation context in NgTemplateOutlet fix: allow context of EmbeddedViewRef to be changed and avoid mutating context in NgTemplateOutlet Jan 23, 2021
@crisbeto crisbeto force-pushed the 24515/embedded-view-ref-context branch from e6e6d67 to f7ff356 Compare January 23, 2021 10:13
@ngbot

This comment has been minimized.

@zarend
Copy link
Contributor

zarend commented Feb 18, 2021

presubmit

@atscott atscott removed the action: presubmit The PR is in need of a google3 presubmit label Feb 19, 2021
Copy link
Contributor

@AndrewKushnir AndrewKushnir left a comment

Choose a reason for hiding this comment

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

Reviewed-for: public-api

@pullapprove pullapprove bot requested a review from jelbourn February 19, 2021 20:23
@@ -93,7 +93,7 @@ export abstract class EmbeddedViewRef<C> extends ViewRef {
/**
* The context for this view, inherited from the anchor element.
*/
abstract get context(): C;
abstract context: C;
Copy link
Member

Choose a reason for hiding this comment

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

Strictly this is a breaking change in the case that this class EmbeddedViewRef was extended. See playground.

Copy link
Member Author

Choose a reason for hiding this comment

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

Do we consider this as something that's meant to be extended, considering that the framework itself constructs it? I believe we've had cases in the past where we've said that constructor changes (also something that would be breaking for extending consumers) aren't breaking.

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure but we could avoid the breaking change by just adding a setter, rather than making it a property.

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 wanted to avoid getters and setters, because they generate more code in ES5.

Copy link
Member

Choose a reason for hiding this comment

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

And actually adding an abstract setter would also be a breaking change!

Copy link
Contributor

Choose a reason for hiding this comment

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

Unless a class is specifically noted that it’s meant to be extended, we don’t consider breaking extenders a breaking change. See #32057 (comment)

Copy link
Member

Choose a reason for hiding this comment

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

So if we are cool that no one outside the framework is going to extend this then I am cool with it

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.

Reviewed-for: public-api

@@ -93,7 +93,7 @@ export abstract class EmbeddedViewRef<C> extends ViewRef {
/**
* The context for this view, inherited from the anchor element.
*/
abstract get context(): C;
abstract context: C;
Copy link
Member

Choose a reason for hiding this comment

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

So if we are cool that no one outside the framework is going to extend this then I am cool with it

Copy link
Contributor

@jessicajaniuk jessicajaniuk left a comment

Choose a reason for hiding this comment

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

reviewed-for: size-tracking

@atscott atscott closed this in a3e1719 Feb 23, 2021
atscott pushed a commit that referenced this pull request Feb 23, 2021
Currently `NgTemplateOutlet` recreates its view if its template is swapped out or a context
object with a different shape is passed in. If an object with the same shape is passed in,
we preserve the old view and we mutate the previous object. This mutation of the original
object can be undesirable if two objects with the same shape are swapped between two
different template outlets.

The current behavior is a result of a limitation in `core` where the `context` of an embedded
view is read-only, however a previous commit made it writeable.

These changes resolve the context mutation issue and clean up a bunch of unnecessary
logic from `NgTemplateOutlet` by taking advantage of the earlier change.

Fixes #24515.

PR Close #40360
@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 Mar 26, 2021
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 area: common Issues related to APIs in the @angular/common package area: core Issues related to the framework runtime cla: yes target: major This PR is targeted for the next major release type: bug/fix
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ngTemplateOutlet modifies its initial context when getting a new context whose shape is the same
6 participants