docs: add detailed example to ContentChildren change (#33499)
PR Close #33499
This commit is contained in:
parent
2b1ba1835e
commit
7355906a95
|
@ -890,6 +890,7 @@ testing/** @angular/fw-test
|
||||||
/aio/content/guide/migration-ngcc.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
/aio/content/guide/migration-ngcc.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
/aio/content/guide/updating-to-version-9.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
/aio/content/guide/updating-to-version-9.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
/aio/content/guide/ivy-compatibility.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
/aio/content/guide/ivy-compatibility.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/ivy-compatibility-examples.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
# ================================================
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
# Appendix: Ivy Compatibility Examples
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<div class="alert is-critical">
|
||||||
|
NOTE: Most of these issues affect a small percentage of applications encountering unusual or rare edge cases.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{@a content-children-descendants}
|
||||||
|
## @ContentChildren Queries Only Match Direct Children By Default
|
||||||
|
|
||||||
|
|
||||||
|
### Basic example of change
|
||||||
|
|
||||||
|
Let's say a component (`Comp`) has a `@ContentChildren` query for `'foo'`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<comp>
|
||||||
|
<div>
|
||||||
|
<div #foo></div> <!-- matches in old runtime, not in new runtime -->
|
||||||
|
</div>
|
||||||
|
</comp>
|
||||||
|
```
|
||||||
|
|
||||||
|
In the previous runtime, the `<div>` with `#foo` would match.
|
||||||
|
With Ivy, that `<div>` does not match because it is not a direct child of `<comp>`.
|
||||||
|
|
||||||
|
|
||||||
|
### Background
|
||||||
|
|
||||||
|
By default, `@ContentChildren` queries have the `descendants` flag set to `false`.
|
||||||
|
|
||||||
|
Previously, "descendants" referred to "descendant directives".
|
||||||
|
An element could be a match as long as there were no other directives between the element and the requesting directive.
|
||||||
|
This made sense for directives with nesting like tabs, where nested tab directives might not be desirable to match.
|
||||||
|
However, this caused surprising behavior for users because adding an unrelated directive like `ngClass` to a wrapper element could invalidate query results.
|
||||||
|
|
||||||
|
For example, with the content query and template below, the last two `Tab` directives would not be matches:
|
||||||
|
|
||||||
|
```
|
||||||
|
@ContentChildren(Tab, {descendants: false}) tabs: QueryList<Tab>
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
<tab-list>
|
||||||
|
<div>
|
||||||
|
<tab> One </tab> <!-- match (nested in element) -->
|
||||||
|
</div>
|
||||||
|
<tab> <!-- match (top level) -->
|
||||||
|
<tab> A </tab> <!-- not a match (nested in tab) -->
|
||||||
|
</tab>
|
||||||
|
<div [ngClass]="classes">
|
||||||
|
<tab> Two </tab> <!-- not a match (nested in ngClass) -->
|
||||||
|
</div>
|
||||||
|
</tab-list>
|
||||||
|
```
|
||||||
|
|
||||||
|
In addition, the differences between type and string predicates were subtle and sometimes unclear.
|
||||||
|
For example, if you replace the type predicate above with a `'foo'` string predicate, the matches change:
|
||||||
|
|
||||||
|
```
|
||||||
|
@ContentChildren('foo', {descendants: false}) foos: QueryList<ElementRef>
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
<tab-list>
|
||||||
|
<div>
|
||||||
|
<div #foo> One </div> <!-- match (nested in element) -->
|
||||||
|
</div>
|
||||||
|
<tab #foo> <!-- match (top level) -->
|
||||||
|
<div #foo> A </div> <!-- match (nested in tab) -->
|
||||||
|
</tab>
|
||||||
|
<div [ngClass]="classes">
|
||||||
|
<div #foo> Two </div> <!-- match (nested in ngClass) -->
|
||||||
|
</div>
|
||||||
|
</tab-list>
|
||||||
|
```
|
||||||
|
|
||||||
|
Because the previous behavior was inconsistent and surprising to users, we did not want to reproduce it in Ivy.
|
||||||
|
Instead, we simplified the mental model so that "descendants" refers to DOM nesting only.
|
||||||
|
Any DOM element between the requesting component and a potential match will invalidate that match.
|
||||||
|
Type predicates and string predicates also have identical matching behavior.
|
||||||
|
|
||||||
|
|
||||||
|
### Example of error
|
||||||
|
|
||||||
|
The error message that you see will depend on how the particular content query is used in the application code.
|
||||||
|
Frequently, an error is thrown when a property is referenced on the content query result (which is now `undefined`).
|
||||||
|
|
||||||
|
For example, if the component sets the content query results to a property, `foos`, `foos.first.bar` would throw the error:
|
||||||
|
|
||||||
|
```
|
||||||
|
Uncaught TypeError: Cannot read property 'bar' of undefined
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
### Recommended fix
|
||||||
|
|
||||||
|
You can either add the `descendants: true` flag to ignore wrapper elements or remove the wrapper elements themselves.
|
||||||
|
|
||||||
|
Option 1:
|
||||||
|
```
|
||||||
|
@ContentChildren('foo', {descendants: true}) foos: QueryList<ElementRef>;
|
||||||
|
```
|
||||||
|
|
||||||
|
Option 2:
|
||||||
|
```
|
||||||
|
<comp>
|
||||||
|
<div #foo></div> <!-- matches in both old runtime and new runtime -->
|
||||||
|
</comp>
|
||||||
|
```
|
|
@ -19,11 +19,9 @@ If the errors are gone, switch back to Ivy by removing the changes to the `tscon
|
||||||
|
|
||||||
|
|
||||||
{@a common-changes}
|
{@a common-changes}
|
||||||
## Changes You May See
|
### Changes You May See
|
||||||
|
|
||||||
Below are a few breaking changes that are more likely than others to be visible as applications are transitioning to Ivy.
|
- 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))
|
||||||
|
|
||||||
- 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).
|
|
||||||
|
|
||||||
- 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).
|
- 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).
|
||||||
|
|
||||||
|
@ -31,9 +29,7 @@ Below are a few breaking changes that are more likely than others to be visible
|
||||||
|
|
||||||
|
|
||||||
{@a less-common-changes}
|
{@a less-common-changes}
|
||||||
## Less Common Changes
|
### Less Common Changes
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
- Properties like `host` inside `@Component` and `@Directive` decorators can be inherited (previously, only properties with explicit field decorators like `@HostBinding` would be inherited).
|
- Properties like `host` inside `@Component` and `@Directive` decorators can be inherited (previously, only properties with explicit field decorators like `@HostBinding` would be inherited).
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue