fix(ivy): support static ContentChild queries (#28811)

This commit adds support for the `static: true` flag in `ContentChild`
queries. Prior to this commit, all `ContentChild` queries were resolved
after change detection ran. This is a problem for backwards
compatibility because View Engine also supported "static" queries which
would resolve before change detection.

Now if users add a `static: true` option, the query will be resolved in
creation mode (before change detection runs). For example:

```ts
@ContentChild(TemplateRef, {static: true}) template !: TemplateRef;
```

This feature will come in handy for components that need
to create components dynamically.

PR Close #28811
This commit is contained in:
Kara Erickson
2019-02-18 18:18:56 -08:00
committed by Igor Minar
parent a4638d5a81
commit 3c1a1620e3
10 changed files with 261 additions and 50 deletions

View File

@ -82,6 +82,7 @@ export {
queryRefresh as ɵqueryRefresh,
viewQuery as ɵviewQuery,
staticViewQuery as ɵstaticViewQuery,
staticContentQuery as ɵstaticContentQuery,
loadViewQuery as ɵloadViewQuery,
contentQuery as ɵcontentQuery,
loadContentQuery as ɵloadContentQuery,

View File

@ -128,6 +128,7 @@ export {
loadViewQuery,
contentQuery,
loadContentQuery,
staticContentQuery
} from './query';
export {

View File

@ -65,6 +65,8 @@ const enum BindingDirection {
*/
export function refreshDescendantViews(lView: LView) {
const tView = lView[TVIEW];
const creationMode = isCreationMode(lView);
// This needs to be set before children are processed to support recursive components
tView.firstTemplatePass = false;
@ -73,7 +75,7 @@ export function refreshDescendantViews(lView: LView) {
// If this is a creation pass, we should not call lifecycle hooks or evaluate bindings.
// This will be done in the update pass.
if (!isCreationMode(lView)) {
if (!creationMode) {
const checkNoChangesMode = getCheckNoChangesMode();
executeInitHooks(lView, tView, checkNoChangesMode);
@ -90,6 +92,13 @@ export function refreshDescendantViews(lView: LView) {
setHostBindings(tView, lView);
}
// We resolve content queries specifically marked as `static` in creation mode. Dynamic
// content queries are resolved during change detection (i.e. update mode), after embedded
// views are refreshed (see block above).
if (creationMode && tView.staticContentQueries) {
refreshContentQueries(tView, lView);
}
refreshChildComponents(tView.components);
}
@ -785,6 +794,7 @@ export function createTView(
expandoInstructions: null,
firstTemplatePass: true,
staticViewQueries: false,
staticContentQueries: false,
initHooks: null,
checkHooks: null,
contentHooks: null,

View File

@ -384,6 +384,14 @@ export interface TView {
*/
staticViewQueries: boolean;
/**
* Whether or not there are any static content queries tracked on this view.
*
* We store this so we know whether or not we should do a content query
* refresh after creation mode to collect static query results.
*/
staticContentQueries: boolean;
/**
* The index where the viewQueries section of `LView` begins. This section contains
* view queries defined for a component/directive.

View File

@ -89,6 +89,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵqueryRefresh': r3.queryRefresh,
'ɵviewQuery': r3.viewQuery,
'ɵstaticViewQuery': r3.staticViewQuery,
'ɵstaticContentQuery': r3.staticContentQuery,
'ɵloadViewQuery': r3.loadViewQuery,
'ɵcontentQuery': r3.contentQuery,
'ɵloadContentQuery': r3.loadContentQuery,

View File

@ -442,7 +442,7 @@ export function loadViewQuery<T>(): T {
*/
export function contentQuery<T>(
directiveIndex: number, predicate: Type<any>| string[], descend: boolean,
// TODO: "read" should be an AbstractType (FW-486)
// TODO(FW-486): "read" should be an AbstractType
read: any): QueryList<T> {
const lView = getLView();
const tView = lView[TVIEW];
@ -459,6 +459,28 @@ export function contentQuery<T>(
return contentQuery;
}
/**
* Registers a QueryList, associated with a static content query, for later refresh
* (part of a view refresh).
*
* @param directiveIndex Current directive index
* @param predicate The type for which the query will search
* @param descend Whether or not to descend into children
* @param read What to save in the query
* @returns QueryList<T>
*/
export function staticContentQuery<T>(
directiveIndex: number, predicate: Type<any>| string[], descend: boolean,
// TODO(FW-486): "read" should be an AbstractType
read: any): void {
const queryList = contentQuery(directiveIndex, predicate, descend, read) as QueryList_<T>;
const tView = getLView()[TVIEW];
queryList._static = true;
if (!tView.staticContentQueries) {
tView.staticContentQueries = true;
}
}
export function loadContentQuery<T>(): QueryList<T> {
const lView = getLView();
ngDevMode &&