fix(ivy): queries should match elements inside ng-container with the descendants: false option (#35384)

Before this change content queries with the `descendants: false` option, as implemented in ivy,
would not descendinto `<ng-container>` elements. This behaviour was different from the way the
View Engine worked. This change alligns ngIvy and VE behaviours when it comes to queries and the
`<ng-container>` elements and fixes a common bugs where a query target was placed inside the
`<ng-container>` element with a * directive on it.

Before:

```html
<needs-target>
  <ng-container *ngIf="condition">
    <div #target>...</div>  <!-- this node would NOT match -->
  </ng-container>
</needs-target>
```

After:

```html
<needs-target>
  <ng-container *ngIf="condition">
    <div #target>...</div>  <!-- this node WILL match -->
  </ng-container>
</needs-target>
```

Fixes #34768

PR Close #35384
This commit is contained in:
Pawel Kozlowski
2020-02-12 14:31:51 +01:00
committed by Alex Rickabaugh
parent 5fbfe6996a
commit 3f4e02b8c7
6 changed files with 272 additions and 81 deletions

View File

@ -188,7 +188,23 @@ class TQuery_ implements TQuery {
private isApplyingToNode(tNode: TNode): boolean {
if (this._appliesToNextNode && this.metadata.descendants === false) {
return this._declarationNodeIndex === (tNode.parent ? tNode.parent.index : -1);
const declarationNodeIdx = this._declarationNodeIndex;
let parent = tNode.parent;
// Determine if a given TNode is a "direct" child of a node on which a content query was
// declared (only direct children of query's host node can match with the descendants: false
// option). There are 3 main use-case / conditions to consider here:
// - <needs-target><i #target></i></needs-target>: here <i #target> parent node is a query
// host node;
// - <needs-target><ng-template [ngIf]="true"><i #target></i></ng-template></needs-target>:
// here <i #target> parent node is null;
// - <needs-target><ng-container><i #target></i></ng-container></needs-target>: here we need
// to go past `<ng-container>` to determine <i #target> parent node (but we shouldn't traverse
// up past the query's host node!).
while (parent !== null && parent.type === TNodeType.ElementContainer &&
parent.index !== declarationNodeIdx) {
parent = parent.parent;
}
return declarationNodeIdx === (parent !== null ? parent.index : -1);
}
return this._appliesToNextNode;
}