Skip to content

Commit 7355906

Browse files
karaAndrewKushnir
authored andcommitted
docs: add detailed example to ContentChildren change (angular#33499)
PR Close angular#33499
1 parent 2b1ba18 commit 7355906

3 files changed

Lines changed: 118 additions & 7 deletions

File tree

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,7 @@ testing/** @angular/fw-test
890890
/aio/content/guide/migration-ngcc.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
891891
/aio/content/guide/updating-to-version-9.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
892892
/aio/content/guide/ivy-compatibility.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
893+
/aio/content/guide/ivy-compatibility-examples.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
893894

894895

895896
# ================================================
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Appendix: Ivy Compatibility Examples
2+
3+
This appendix is intended to provide more background on Ivy changes. Many of these examples list error messages you may see, so searching by error message might be a good idea if you are debugging.
4+
5+
<div class="alert is-critical">
6+
NOTE: Most of these issues affect a small percentage of applications encountering unusual or rare edge cases.
7+
</div>
8+
9+
10+
{@a content-children-descendants}
11+
## @ContentChildren Queries Only Match Direct Children By Default
12+
13+
14+
### Basic example of change
15+
16+
Let's say a component (`Comp`) has a `@ContentChildren` query for `'foo'`:
17+
18+
```html
19+
<comp>
20+
<div>
21+
<div #foo></div> <!-- matches in old runtime, not in new runtime -->
22+
</div>
23+
</comp>
24+
```
25+
26+
In the previous runtime, the `<div>` with `#foo` would match.
27+
With Ivy, that `<div>` does not match because it is not a direct child of `<comp>`.
28+
29+
30+
### Background
31+
32+
By default, `@ContentChildren` queries have the `descendants` flag set to `false`.
33+
34+
Previously, "descendants" referred to "descendant directives".
35+
An element could be a match as long as there were no other directives between the element and the requesting directive.
36+
This made sense for directives with nesting like tabs, where nested tab directives might not be desirable to match.
37+
However, this caused surprising behavior for users because adding an unrelated directive like `ngClass` to a wrapper element could invalidate query results.
38+
39+
For example, with the content query and template below, the last two `Tab` directives would not be matches:
40+
41+
```
42+
@ContentChildren(Tab, {descendants: false}) tabs: QueryList<Tab>
43+
```
44+
45+
```
46+
<tab-list>
47+
<div>
48+
<tab> One </tab> <!-- match (nested in element) -->
49+
</div>
50+
<tab> <!-- match (top level) -->
51+
<tab> A </tab> <!-- not a match (nested in tab) -->
52+
</tab>
53+
<div [ngClass]="classes">
54+
<tab> Two </tab> <!-- not a match (nested in ngClass) -->
55+
</div>
56+
</tab-list>
57+
```
58+
59+
In addition, the differences between type and string predicates were subtle and sometimes unclear.
60+
For example, if you replace the type predicate above with a `'foo'` string predicate, the matches change:
61+
62+
```
63+
@ContentChildren('foo', {descendants: false}) foos: QueryList<ElementRef>
64+
```
65+
66+
```
67+
<tab-list>
68+
<div>
69+
<div #foo> One </div> <!-- match (nested in element) -->
70+
</div>
71+
<tab #foo> <!-- match (top level) -->
72+
<div #foo> A </div> <!-- match (nested in tab) -->
73+
</tab>
74+
<div [ngClass]="classes">
75+
<div #foo> Two </div> <!-- match (nested in ngClass) -->
76+
</div>
77+
</tab-list>
78+
```
79+
80+
Because the previous behavior was inconsistent and surprising to users, we did not want to reproduce it in Ivy.
81+
Instead, we simplified the mental model so that "descendants" refers to DOM nesting only.
82+
Any DOM element between the requesting component and a potential match will invalidate that match.
83+
Type predicates and string predicates also have identical matching behavior.
84+
85+
86+
### Example of error
87+
88+
The error message that you see will depend on how the particular content query is used in the application code.
89+
Frequently, an error is thrown when a property is referenced on the content query result (which is now `undefined`).
90+
91+
For example, if the component sets the content query results to a property, `foos`, `foos.first.bar` would throw the error:
92+
93+
```
94+
Uncaught TypeError: Cannot read property 'bar' of undefined
95+
```
96+
97+
If you see an error like this, and the `undefined` property refers to the result of a `@ContentChildren` query, it may well be caused by this change.
98+
99+
100+
### Recommended fix
101+
102+
You can either add the `descendants: true` flag to ignore wrapper elements or remove the wrapper elements themselves.
103+
104+
Option 1:
105+
```
106+
@ContentChildren('foo', {descendants: true}) foos: QueryList<ElementRef>;
107+
```
108+
109+
Option 2:
110+
```
111+
<comp>
112+
<div #foo></div> <!-- matches in both old runtime and new runtime -->
113+
</comp>
114+
```

aio/content/guide/ivy-compatibility.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,17 @@ If the errors are gone, switch back to Ivy by removing the changes to the `tscon
1919

2020

2121
{@a common-changes}
22-
## Changes You May See
22+
### Changes You May See
2323

24-
Below are a few breaking changes that are more likely than others to be visible as applications are transitioning to Ivy.
25-
26-
- By default, `@ContentChildren` queries will only search direct child nodes in the DOM hierarchy (previously, they would search any nesting level in the DOM as long as another directive wasn't matched above it).
24+
- By default, `@ContentChildren` queries will only search direct child nodes in the DOM hierarchy (previously, they would search any nesting level in the DOM as long as another directive wasn't matched above it). ([details](guide/ivy-compatibility-examples#content-children-descendants))
2725

2826
- All classes that use Angular DI must have an Angular decorator like `@Directive()` or `@Injectable` (previously, undecorated classes were allowed if an ancestor class or subclass had a decorator).
2927

3028
- Unbound inputs for directives (e.g. name in `<my-comp name="">`) are now set upon creation of the view, before change detection runs (previously, all inputs were set during change detection).
3129

3230

3331
{@a less-common-changes}
34-
## Less Common Changes
35-
36-
The following changes will be visible more rarely, as they mostly deal in edge cases or unspecified behavior that is not part of our public API.
32+
### Less Common Changes
3733

3834
- Properties like `host` inside `@Component` and `@Directive` decorators can be inherited (previously, only properties with explicit field decorators like `@HostBinding` would be inherited).
3935

0 commit comments

Comments
 (0)