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

Project root node of @if and @for blocks #52414

Closed
wants to merge 2 commits into from

Conversation

crisbeto
Copy link
Member

A couple of fixes to get the content projection behavior of @if and @for blocks closer to *ngIf and *ngFor. Includes the following commits:

fix(compiler): project control flow root elements into correct slot

With the directive-based control flow users were able to conditionally project content using the * syntax. E.g. <div *ngIf="expr" projectMe></div> will be projected into <ng-content select="[projectMe]"/>, because the attributes and tag name from the div are copied to the template via the template creation instruction. With @if and @for that is not the case, because the conditional is placed around elements, rather than on them. The result is that content projection won't work in the same way if a user converts from *ngIf to @if.

These changes aim to cover the most common case by doing the same copying when a control flow node has one and only one root element or template node.

This approach comes with some caveats:

  1. As soon as any other node is added to the root, the copying behavior won't work anymore. A diagnostic will be added to flag cases like this and to explain how to work around it.
  2. If preserveWhitespaces is enabled, it's very likely that indentation will break this workaround, because it'll include an additional text node as the first child. We can work around it here, but in a discussion it was decided not to, because the user explicitly opted into preserving the whitespace and we would have to drop it from the generated code. The diagnostic mentioned point 1 will flag such cases to users.

refactor(compiler): implement control flow content projection fix in template pipeline

Recreates the fix for content projection in control flow in the new template pipeline. I also had to make the following adjustments to the pipeline:

  1. The TemplateOp.tag property was being used to generate the name of the template function, rather than the actual tag name being passed into ɵɵtemplate. Since the content projection fix requires the tag name to be passed in, I've introduced a new functionNameSuffix property instead.
  2. TemplateOp.block was being used to determine whether to pass TemplateOp.tag into the ɵɵtemplate instruction. Now that we're always passing in the tag name after the refactor in point 1, we no longer need this flag.

In addition to the refactors above, I also made some minor cleanups where I saw the opportunity to do so.

Fixes #52277.

@crisbeto crisbeto added action: review The PR is still awaiting reviews from at least one requested reviewer area: compiler Issues related to `ngc`, Angular's template compiler target: rc This PR is targeted for the next release-candidate labels Oct 27, 2023
@crisbeto crisbeto added this to the v17-final milestone Oct 27, 2023
*/
block: boolean;
functionNameSuffix: string|null;
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'm not super attached to the name so I'm open to suggestions. The main goal was to stop using the tag name for the function name generation.

packages/compiler/src/template/pipeline/src/ingest.ts Outdated Show resolved Hide resolved
@crisbeto crisbeto marked this pull request as ready for review October 27, 2023 08:51
@crisbeto
Copy link
Member Author

I've pushed some changes to avoid future issues if we start capturing comments by default and to rebase.

Copy link
Member

@pkozlowski-opensource pkozlowski-opensource left a comment

Choose a reason for hiding this comment

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

LGTM overall but I would like to revisit this assumption:

If preserveWhitespaces is enabled, it's very likely that indentation will break this

  • workaround, because it'll include an additional text node as the first child. We can work
  • around it here, but in a discussion it was decided not to, because the user explicitly opted
  • into preserving the whitespace and we would have to drop it from the generated code.
  • The diagnostic mentioned point transpiler: show line numbers that have errors #1 will flag such cases to users.

I would say that we should have just one rule: we opt out of the root node capturing if there is more than one root node. This will be more common case with preserveWhitespaces but still there is one consistent rule here.

@crisbeto
Copy link
Member Author

Feedback has been addressed.

Copy link
Member

@pkozlowski-opensource pkozlowski-opensource left a comment

Choose a reason for hiding this comment

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

LGTM, thnx!

Copy link
Contributor

@dylhunn dylhunn left a comment

Choose a reason for hiding this comment

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

LGTM. Makes perfect sense to me, and thank you for also updating the template pipeline. A lot of the use of tag for control flow and conditionals was marginal to begin with -- it was more designed for elements and containers.

packages/compiler/src/template/pipeline/src/ingest.ts Outdated Show resolved Hide resolved
state, compatibility);
}
const repeaterToken =
op.tag === null ? '' : '_' + prefixWithNamespace(op.tag, op.namespace);
Copy link
Contributor

Choose a reason for hiding this comment

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

I kind of preferred having this prefixing logic in the naming phase as opposed to ingest, where you've now folded it into the new suffix field -- it seems like it's logically part of naming. It's a weak preference though. I don't think we could conceivably need to use the raw suffix for anything else, so it's probably fine?

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 agree that it would've felt more appropriate to have the naming logic there, but the rules we have for the function naming are a bit too different for the template nodes vs control flow that I couldn't find a better place than this one. I think that once we remove the definition builder, we can revisit the naming scheme.

@crisbeto crisbeto added action: merge The PR is ready for merge by the caretaker and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Oct 30, 2023
With the directive-based control flow users were able to conditionally project content using the `*` syntax. E.g. `<div *ngIf="expr" projectMe></div>` will be projected into `<ng-content select="[projectMe]"/>`, because the attributes and tag name from the `div` are copied to the template via the template creation instruction. With `@if` and `@for` that is not the case, because the conditional is placed *around* elements, rather than *on* them. The result is that content projection won't work in the same way if a user converts from `*ngIf` to `@if`.

These changes aim to cover the most common case by doing the same copying when a control flow node has *one and only one* root element or template node.

This approach comes with some caveats:
1. As soon as any other node is added to the root, the copying behavior won't work anymore. A diagnostic will be added to flag cases like this and to explain how to work around it.
2. If `preserveWhitespaces` is enabled, it's very likely that indentation will break this workaround, because it'll include an additional text node as the first child. We can work around it here, but in a discussion it was decided not to, because the user explicitly opted into preserving the whitespace and we would have to drop it from the generated code. The diagnostic mentioned point angular#1 will flag such cases to users.

Fixes angular#52277.
…template pipeline

Recreates the fix for content projection in control flow in the new template pipeline. I also had to make the following adjustments to the pipeline:
1. The `TemplateOp.tag` property was being used to generate the name of the template function, rather than the actual tag name being passed into `ɵɵtemplate`. Since the content projection fix requires the tag name to be passed in, I've introduced a new `functionNameSuffix` property instead.
2. `TemplateOp.block` was being used to determine whether to pass `TemplateOp.tag` into the `ɵɵtemplate` instruction. Now that we're always passing in the tag name after the refactor in point 1, we no longer need this flag.

In addition to the refactors above, I also made some minor cleanups where I saw the opportunity to do so.
@crisbeto crisbeto added the merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note label Oct 31, 2023
@crisbeto
Copy link
Member Author

crisbeto commented Oct 31, 2023

Caretaker note: the aio-local failure is unrelated and is present in the main branch as well.

@alxhub
Copy link
Member

alxhub commented Oct 31, 2023

This PR was merged into the repository by commit c7b730e.

alxhub pushed a commit that referenced this pull request Oct 31, 2023
…52414)

With the directive-based control flow users were able to conditionally project content using the `*` syntax. E.g. `<div *ngIf="expr" projectMe></div>` will be projected into `<ng-content select="[projectMe]"/>`, because the attributes and tag name from the `div` are copied to the template via the template creation instruction. With `@if` and `@for` that is not the case, because the conditional is placed *around* elements, rather than *on* them. The result is that content projection won't work in the same way if a user converts from `*ngIf` to `@if`.

These changes aim to cover the most common case by doing the same copying when a control flow node has *one and only one* root element or template node.

This approach comes with some caveats:
1. As soon as any other node is added to the root, the copying behavior won't work anymore. A diagnostic will be added to flag cases like this and to explain how to work around it.
2. If `preserveWhitespaces` is enabled, it's very likely that indentation will break this workaround, because it'll include an additional text node as the first child. We can work around it here, but in a discussion it was decided not to, because the user explicitly opted into preserving the whitespace and we would have to drop it from the generated code. The diagnostic mentioned point #1 will flag such cases to users.

Fixes #52277.

PR Close #52414
atscott pushed a commit that referenced this pull request Nov 6, 2023
…rol flow (#52515)

Discovered this while validating #52414 against Angular Material. We were projecting `<ng-template>` nodes at the root of `@if` and `@for` with the `ng-template` tag name which enables directive matching and applies the directive to the control flow node.

These changes fix the issue by never passing along the `ng-template` tag name.

PR Close #52515
atscott pushed a commit that referenced this pull request Nov 6, 2023
…rol flow (#52515)

Discovered this while validating #52414 against Angular Material. We were projecting `<ng-template>` nodes at the root of `@if` and `@for` with the `ng-template` tag name which enables directive matching and applies the directive to the control flow node.

These changes fix the issue by never passing along the `ng-template` tag name.

PR Close #52515
crisbeto added a commit to crisbeto/angular that referenced this pull request Nov 9, 2023
…ent projection

This is a follow-up to the fix from angular#52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
crisbeto added a commit to crisbeto/angular that referenced this pull request Nov 9, 2023
…ent projection

This is a follow-up to the fix from angular#52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
crisbeto added a commit to crisbeto/angular that referenced this pull request Nov 9, 2023
…ent projection

This is a follow-up to the fix from angular#52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
crisbeto added a commit to crisbeto/angular that referenced this pull request Nov 9, 2023
…ent projection

This is a follow-up to the fix from angular#52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
crisbeto added a commit to crisbeto/angular that referenced this pull request Nov 9, 2023
…ent projection

This is a follow-up to the fix from angular#52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
crisbeto added a commit to crisbeto/angular that referenced this pull request Nov 14, 2023
…ent projection

This is a follow-up to the fix from angular#52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
crisbeto added a commit to crisbeto/angular that referenced this pull request Nov 16, 2023
…ent projection

This is a follow-up to the fix from angular#52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
crisbeto added a commit to crisbeto/angular that referenced this pull request Nov 16, 2023
…ent projection

This is a follow-up to the fix from angular#52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
crisbeto added a commit to crisbeto/angular that referenced this pull request Nov 16, 2023
…ent projection

This is a follow-up to the fix from angular#52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
crisbeto added a commit to crisbeto/angular that referenced this pull request Nov 16, 2023
…ent projection

This is a follow-up to the fix from angular#52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
AndrewKushnir pushed a commit that referenced this pull request Nov 17, 2023
…ent projection (#52726)

This is a follow-up to the fix from #52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.

PR Close #52726
AndrewKushnir pushed a commit that referenced this pull request Nov 17, 2023
…ent projection (#52726)

This is a follow-up to the fix from #52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.

PR Close #52726
crisbeto added a commit to crisbeto/angular that referenced this pull request Nov 27, 2023
…ent projection

This is a follow-up to the fix from angular#52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
crisbeto added a commit to crisbeto/angular that referenced this pull request Nov 27, 2023
…ent projection

This is a follow-up to the fix from angular#52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
pkozlowski-opensource pushed a commit that referenced this pull request Nov 28, 2023
…ent projection (#53190)

This is a follow-up to the fix from #52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.

PR Close #53190
Tweniee pushed a commit to Tweniee/angular that referenced this pull request Nov 29, 2023
…ent projection (angular#53190)

This is a follow-up to the fix from angular#52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.

PR Close angular#53190
@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 Dec 1, 2023
tbondwilkinson pushed a commit to tbondwilkinson/angular that referenced this pull request Dec 6, 2023
…ngular#52414)

With the directive-based control flow users were able to conditionally project content using the `*` syntax. E.g. `<div *ngIf="expr" projectMe></div>` will be projected into `<ng-content select="[projectMe]"/>`, because the attributes and tag name from the `div` are copied to the template via the template creation instruction. With `@if` and `@for` that is not the case, because the conditional is placed *around* elements, rather than *on* them. The result is that content projection won't work in the same way if a user converts from `*ngIf` to `@if`.

These changes aim to cover the most common case by doing the same copying when a control flow node has *one and only one* root element or template node.

This approach comes with some caveats:
1. As soon as any other node is added to the root, the copying behavior won't work anymore. A diagnostic will be added to flag cases like this and to explain how to work around it.
2. If `preserveWhitespaces` is enabled, it's very likely that indentation will break this workaround, because it'll include an additional text node as the first child. We can work around it here, but in a discussion it was decided not to, because the user explicitly opted into preserving the whitespace and we would have to drop it from the generated code. The diagnostic mentioned point angular#1 will flag such cases to users.

Fixes angular#52277.

PR Close angular#52414
tbondwilkinson pushed a commit to tbondwilkinson/angular that referenced this pull request Dec 6, 2023
…template pipeline (angular#52414)

Recreates the fix for content projection in control flow in the new template pipeline. I also had to make the following adjustments to the pipeline:
1. The `TemplateOp.tag` property was being used to generate the name of the template function, rather than the actual tag name being passed into `ɵɵtemplate`. Since the content projection fix requires the tag name to be passed in, I've introduced a new `functionNameSuffix` property instead.
2. `TemplateOp.block` was being used to determine whether to pass `TemplateOp.tag` into the `ɵɵtemplate` instruction. Now that we're always passing in the tag name after the refactor in point 1, we no longer need this flag.

In addition to the refactors above, I also made some minor cleanups where I saw the opportunity to do so.

PR Close angular#52414
ChellappanRajan pushed a commit to ChellappanRajan/angular that referenced this pull request Jan 23, 2024
…ngular#52414)

With the directive-based control flow users were able to conditionally project content using the `*` syntax. E.g. `<div *ngIf="expr" projectMe></div>` will be projected into `<ng-content select="[projectMe]"/>`, because the attributes and tag name from the `div` are copied to the template via the template creation instruction. With `@if` and `@for` that is not the case, because the conditional is placed *around* elements, rather than *on* them. The result is that content projection won't work in the same way if a user converts from `*ngIf` to `@if`.

These changes aim to cover the most common case by doing the same copying when a control flow node has *one and only one* root element or template node.

This approach comes with some caveats:
1. As soon as any other node is added to the root, the copying behavior won't work anymore. A diagnostic will be added to flag cases like this and to explain how to work around it.
2. If `preserveWhitespaces` is enabled, it's very likely that indentation will break this workaround, because it'll include an additional text node as the first child. We can work around it here, but in a discussion it was decided not to, because the user explicitly opted into preserving the whitespace and we would have to drop it from the generated code. The diagnostic mentioned point angular#1 will flag such cases to users.

Fixes angular#52277.

PR Close angular#52414
ChellappanRajan pushed a commit to ChellappanRajan/angular that referenced this pull request Jan 23, 2024
…template pipeline (angular#52414)

Recreates the fix for content projection in control flow in the new template pipeline. I also had to make the following adjustments to the pipeline:
1. The `TemplateOp.tag` property was being used to generate the name of the template function, rather than the actual tag name being passed into `ɵɵtemplate`. Since the content projection fix requires the tag name to be passed in, I've introduced a new `functionNameSuffix` property instead.
2. `TemplateOp.block` was being used to determine whether to pass `TemplateOp.tag` into the `ɵɵtemplate` instruction. Now that we're always passing in the tag name after the refactor in point 1, we no longer need this flag.

In addition to the refactors above, I also made some minor cleanups where I saw the opportunity to do so.

PR Close angular#52414
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: compiler Issues related to `ngc`, Angular's template compiler merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note target: rc This PR is targeted for the next release-candidate
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Angular Material Form Unexpected Behavior After Control Flow Migration
5 participants